From f9725b13078f52838353f909b508431d2fb38faa Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 3 Feb 2025 14:02:49 +0100 Subject: [PATCH 001/212] JDK-8344942 --- .../lib/template_framework/Frame.java | 72 +++++++++++ .../compiler/lib/template_framework/Hook.java | 26 ++++ .../lib/template_framework/HookInsert.java | 26 ++++ .../InstantiatedTemplate.java | 26 ++++ .../compiler/lib/template_framework/README.md | 4 + .../lib/template_framework/Renderer.java | 113 ++++++++++++++++++ .../template_framework/RendererException.java | 30 +++++ .../lib/template_framework/StaticHelpers.java | 46 +++++++ .../lib/template_framework/Template.java | 59 +++++++++ .../lib/template_framework/TemplateUse.java | 70 +++++++++++ .../examples/TestParameters.java | 78 ++++++++++++ 11 files changed, 550 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/Frame.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/Hook.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/HookInsert.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/README.md create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/RendererException.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/StaticHelpers.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/Template.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/TemplateUse.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestParameters.java diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java new file mode 100644 index 0000000000000..d86882e4fb1c4 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java @@ -0,0 +1,72 @@ +/* + * 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; + +import java.util.HashMap; +import java.util.Map; + +class Frame { + private final StringBuilder builder = new StringBuilder(); + private final Map hookInsertionIndex = new HashMap<>(); + private final Map variableNames = new HashMap<>(); + private final Map context = new HashMap<>(); + + void addString(String s) { + builder.append(s); + } + + void addHook(Hook hook) { + hookInsertionIndex.put(hook, builder.length()); + } + + boolean hasHook(Hook hook) { + return hookInsertionIndex.containsKey(hook); + } + + void insertIntoHook(Hook hook, String s) { + int index = hookInsertionIndex.get(hook); + builder.insert(index, s); + hookInsertionIndex.put(hook, index + s.length()); + } + + public void addContext(String key, String value) { + context.put(key, value); + } + + public String getContext(String key) { + if (context.containsKey(key)) { + return context.get(key); + } + throw new RendererException("Tried to interpolate field " + key + " which does not exist."); + } + + String variableName(String name) { + return variableNames.computeIfAbsent(name, s -> name + Renderer.variableId++); + } + + @Override + public String toString() { + return builder.toString(); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java new file mode 100644 index 0000000000000..d56424fd6e98d --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java @@ -0,0 +1,26 @@ +/* + * 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; + +public record Hook(String name) {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/HookInsert.java b/test/hotspot/jtreg/compiler/lib/template_framework/HookInsert.java new file mode 100644 index 0000000000000..32e8f5a434f0a --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/HookInsert.java @@ -0,0 +1,26 @@ +/* + * 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; + +public record HookInsert(Hook hook, TemplateUse templateUse) {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java b/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java new file mode 100644 index 0000000000000..b0c1e1e29bbfa --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java @@ -0,0 +1,26 @@ +/* + * 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; + +public record InstantiatedTemplate(Object[] elements) {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/README.md b/test/hotspot/jtreg/compiler/lib/template_framework/README.md new file mode 100644 index 0000000000000..5ec4d4500f092 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/README.md @@ -0,0 +1,4 @@ +# Template Framework +The Template Framework allows the generation of code with Templates. The goal is that these Templates are easy to write, and allow regression tests to cover a larger scope, and to make temlate based fuzzing easy to extend. + +TODO diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java new file mode 100644 index 0000000000000..cca005f811bbc --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -0,0 +1,113 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.regex.MatchResult; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +public abstract class Renderer { + public static final class Nothing { + public static final Nothing instance = new Nothing(); + + private Nothing() {} + } + + private static final Pattern variableNamePattern = Pattern.compile("\\$([a-zA-Z_][a-zA-Z0-9_]*)"); + private static final Pattern interpolationPattern = Pattern.compile("#([a-zA-Z_][a-zA-Z0-9_]*)"); + private static final ArrayList stack = new ArrayList(); + + static int variableId = 0; + + public static String render(TemplateUse templateUse) { + return visit(templateUse); + } + + public static String $(String name) { + return currentStackFrame().variableName(name); + } + + public static Nothing let(String key, Object value) { + currentStackFrame().addContext(key, value.toString()); + return Nothing.instance; + } + + public static R let(String key, T value, Function block) { + currentStackFrame().addContext(key, value.toString()); + return block.apply(value); + } + + public static int depth() { + return stack.size(); + } + + private static Frame currentStackFrame() { + if (stack.isEmpty()) { + throw new RendererException("A method such as $ or let was called outside a template rendering. Make sure you are not calling templates yourself, but use use()."); + } + return stack.getLast(); + } + + private static String visit(TemplateUse templateUse) { + Frame frame = new Frame(); + stack.add(frame); + templateUse.visitArguments((name, value) -> frame.addContext(name, value.toString())); + InstantiatedTemplate it = templateUse.instantiate(); + for (Object i : it.elements()) { + switch (i) { + case Nothing x -> {} + case String s -> frame.addString(templateString(s, frame)); + case Integer s -> frame.addString(s.toString()); + case Long s -> frame.addString(s.toString()); + case Double s -> frame.addString(s.toString()); + case Float s -> frame.addString(s.toString()); + case Hook h -> frame.addHook(h); + case HookInsert h -> + frameForHook(h.hook()).insertIntoHook(h.hook(), visit(h.templateUse())); + case TemplateUse t -> frame.addString(visit(t)); + default -> throw new RendererException("body contained unexpected element: " + i); + } + } + stack.removeLast(); + return frame.toString(); + } + + private static String templateString(String s, Frame frame) { + var temp = variableNamePattern.matcher(s).replaceAll((MatchResult result) -> $(result.group(1))); + return interpolationPattern.matcher(temp).replaceAll((MatchResult result) -> frame.getContext(result.group(1))); + } + + private static Frame frameForHook(Hook hook) { + for (int i = stack.size() - 1; i >= 0; i--) { + Frame frame = stack.get(i); + if (frame.hasHook(hook)) { + return frame; + } + } + throw new RuntimeException("hook " + hook.name() + " was referenced but not found!"); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/RendererException.java b/test/hotspot/jtreg/compiler/lib/template_framework/RendererException.java new file mode 100644 index 0000000000000..4ec1c8f4da979 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/RendererException.java @@ -0,0 +1,30 @@ +/* + * 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; + +public class RendererException extends RuntimeException { + public RendererException(String message) { + super(message); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/StaticHelpers.java b/test/hotspot/jtreg/compiler/lib/template_framework/StaticHelpers.java new file mode 100644 index 0000000000000..2f7a2d5558753 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/StaticHelpers.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; + +public interface StaticHelpers { + static InstantiatedTemplate body(Object... tokens) { + return new InstantiatedTemplate(tokens); + } + + static TemplateUse use(Template.ZeroArg t) { + return new TemplateUse.ZeroArgUse(t); + } + + static TemplateUse.OneArgUse use(Template.OneArg t, A a) { + return new TemplateUse.OneArgUse<>(t, a); + } + + static TemplateUse.TwoArgsUse use(Template.TwoArgs t, A a, B b) { + return new TemplateUse.TwoArgsUse<>(t, a, b); + } + + static HookInsert intoHook(Hook hook, TemplateUse t) { + return new HookInsert(hook, t); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java new file mode 100644 index 0000000000000..2fa19346024e6 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -0,0 +1,59 @@ +/* + * 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; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface Template { + static ZeroArg make(ZeroArg t) { + return t; + } + + static OneArg make(String arg0Name, Function t) { + return new OneArg<>(arg0Name, t); + } + + static TwoArgs make(String arg0Name, String arg1Name, BiFunction t) { + return new TwoArgs<>(arg0Name, arg1Name, t); + } + + @FunctionalInterface + interface ZeroArg extends Template { + InstantiatedTemplate instantiate(); + } + + record OneArg(String arg0Name, Function function) implements Template { + InstantiatedTemplate instantiate(A a) { + return function.apply(a); + } + } + + record TwoArgs(String arg0Name, String arg1Name, + BiFunction function) implements Template { + InstantiatedTemplate instantiate(A a, B b) { + return function.apply(a, b); + } + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateUse.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateUse.java new file mode 100644 index 0000000000000..7209804625b92 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateUse.java @@ -0,0 +1,70 @@ +/* + * 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; + +public interface TemplateUse { + record ZeroArgUse(Template.ZeroArg zeroArg) implements TemplateUse { + @Override + public InstantiatedTemplate instantiate() { + return zeroArg.instantiate(); + } + + @Override + public void visitArguments(ArgumentVisitor visitor) {} + } + + record OneArgUse(Template.OneArg oneArg, A a) implements TemplateUse { + @Override + public InstantiatedTemplate instantiate() { + return oneArg.instantiate(a); + } + + @Override + public void visitArguments(ArgumentVisitor visitor) { + visitor.visit(oneArg.arg0Name(), a); + } + } + + record TwoArgsUse(Template.TwoArgs twoArgs, A a, B b) implements TemplateUse { + @Override + public InstantiatedTemplate instantiate() { + return twoArgs.instantiate(a, b); + } + + @Override + public void visitArguments(ArgumentVisitor visitor) { + visitor.visit(twoArgs.arg0Name(), a); + visitor.visit(twoArgs.arg1Name(), b); + } + } + + InstantiatedTemplate instantiate(); + + @FunctionalInterface + interface ArgumentVisitor { + void visit(String name, Object value); + } + + void visitArguments(ArgumentVisitor visitor); +} diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestParameters.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestParameters.java new file mode 100644 index 0000000000000..5b1b9b38df1aa --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestParameters.java @@ -0,0 +1,78 @@ +/* + * 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 + * @summary Example test with parameters. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver template_framework.examples.TestParameters + */ + +package template_framework.examples; + +import compiler.lib.compile_framework.*; +import compiler.lib.template_framework.*; +import static compiler.lib.template_framework.Renderer.*; +import static compiler.lib.template_framework.StaticHelpers.*; + +public class TestParameters { + + public static void main(String[] args) { + // 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(); + + // Object ret = p.xyz.InnterTest.test(); + Object ret = comp.invoke("p.xyz.InnerTest", "test", new Object[] {}); + System.out.println("res: " + ret); + + // Check that the return value is the sum of the two parameters. + if ((42 + 7) != (int)ret) { + throw new RuntimeException("Unexpected result"); + } + } + + // Generate a source Java file as String + public static String generate() { + // Create a Template with two parameter holes. + var template = Template.make("param1", "param2", (String param1, String param2) -> body( + """ + package p.xyz; + public class InnerTest { + public static int test() { + return #param1 + #param2; + } + } + """ + )); + + // The two parameter holes are to be replaced with the provided values. + return Renderer.render(use(template, "42", "7")); + } +} From 87e1b1228ff4ceb28d58a33bc0a0b2946534221c Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 3 Feb 2025 15:55:06 +0100 Subject: [PATCH 002/212] withArgs instead of use --- .../lib/template_framework/StaticHelpers.java | 12 ---------- .../lib/template_framework/Template.java | 24 +++++++++++++++---- .../lib/template_framework/TemplateUse.java | 4 ++-- .../examples/TestParameters.java | 2 +- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/StaticHelpers.java b/test/hotspot/jtreg/compiler/lib/template_framework/StaticHelpers.java index 2f7a2d5558753..77facdc799999 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/StaticHelpers.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/StaticHelpers.java @@ -28,18 +28,6 @@ static InstantiatedTemplate body(Object... tokens) { return new InstantiatedTemplate(tokens); } - static TemplateUse use(Template.ZeroArg t) { - return new TemplateUse.ZeroArgUse(t); - } - - static TemplateUse.OneArgUse use(Template.OneArg t, A a) { - return new TemplateUse.OneArgUse<>(t, a); - } - - static TemplateUse.TwoArgsUse use(Template.TwoArgs t, A a, B b) { - return new TemplateUse.TwoArgsUse<>(t, a, b); - } - static HookInsert intoHook(Hook hook, TemplateUse t) { return new HookInsert(hook, t); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 2fa19346024e6..4666997f41272 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -25,10 +25,11 @@ import java.util.function.BiFunction; import java.util.function.Function; +import java.util.function.Supplier; public interface Template { - static ZeroArg make(ZeroArg t) { - return t; + static ZeroArgs make(Supplier t) { + return new ZeroArgs(t); } static OneArg make(String arg0Name, Function t) { @@ -39,15 +40,24 @@ static TwoArgs make(String arg0Name, String arg1Name, BiFunction(arg0Name, arg1Name, t); } - @FunctionalInterface - interface ZeroArg extends Template { - InstantiatedTemplate instantiate(); + record ZeroArgs(Supplier function) implements Template { + InstantiatedTemplate instantiate() { + return function.get(); + } + + public TemplateUse.ZeroArgsUse withArgs() { + return new TemplateUse.ZeroArgsUse(this); + } } record OneArg(String arg0Name, Function function) implements Template { InstantiatedTemplate instantiate(A a) { return function.apply(a); } + + public TemplateUse.OneArgUse withArgs(A a) { + return new TemplateUse.OneArgUse<>(this, a); + } } record TwoArgs(String arg0Name, String arg1Name, @@ -55,5 +65,9 @@ record TwoArgs(String arg0Name, String arg1Name, InstantiatedTemplate instantiate(A a, B b) { return function.apply(a, b); } + + public TemplateUse.TwoArgsUse withArgs(A a, B b) { + return new TemplateUse.TwoArgsUse<>(this, a, b); + } } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateUse.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateUse.java index 7209804625b92..94f3599d381b6 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateUse.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateUse.java @@ -24,10 +24,10 @@ package compiler.lib.template_framework; public interface TemplateUse { - record ZeroArgUse(Template.ZeroArg zeroArg) implements TemplateUse { + record ZeroArgsUse(Template.ZeroArgs zeroArgs) implements TemplateUse { @Override public InstantiatedTemplate instantiate() { - return zeroArg.instantiate(); + return zeroArgs.instantiate(); } @Override diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestParameters.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestParameters.java index 5b1b9b38df1aa..f5a22a0123311 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestParameters.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestParameters.java @@ -73,6 +73,6 @@ public static int test() { )); // The two parameter holes are to be replaced with the provided values. - return Renderer.render(use(template, "42", "7")); + return Renderer.render(template.withArgs("42", "7")); } } From c7d0828591e5b342a6bbf9a0c1949f264b794998 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 3 Feb 2025 16:29:41 +0100 Subject: [PATCH 003/212] more testing and refactoring --- .../lib/template_framework/StaticHelpers.java | 34 -------- .../lib/template_framework/Template.java | 8 ++ .../lib/template_framework/TemplateUse.java | 8 ++ ...TestParameters.java => TestArguments.java} | 23 +++--- .../tests/TestTemplate.java | 81 +++++++++++++++++++ 5 files changed, 108 insertions(+), 46 deletions(-) delete mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/StaticHelpers.java rename test/hotspot/jtreg/testlibrary_tests/template_framework/examples/{TestParameters.java => TestArguments.java} (74%) create mode 100644 test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/StaticHelpers.java b/test/hotspot/jtreg/compiler/lib/template_framework/StaticHelpers.java deleted file mode 100644 index 77facdc799999..0000000000000 --- a/test/hotspot/jtreg/compiler/lib/template_framework/StaticHelpers.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package compiler.lib.template_framework; - -public interface StaticHelpers { - static InstantiatedTemplate body(Object... tokens) { - return new InstantiatedTemplate(tokens); - } - - static HookInsert intoHook(Hook hook, TemplateUse t) { - return new HookInsert(hook, t); - } -} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 4666997f41272..378615f4180a2 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -70,4 +70,12 @@ public TemplateUse.TwoArgsUse withArgs(A a, B b) { return new TemplateUse.TwoArgsUse<>(this, a, b); } } + + static InstantiatedTemplate body(Object... tokens) { + return new InstantiatedTemplate(tokens); + } + + static HookInsert intoHook(Hook hook, TemplateUse t) { + return new HookInsert(hook, t); + } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateUse.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateUse.java index 94f3599d381b6..083eeb7614b30 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateUse.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateUse.java @@ -67,4 +67,12 @@ interface ArgumentVisitor { } void visitArguments(ArgumentVisitor visitor); + + // TODO ensure that there can be no nested rendering! + // Otherwise people might fold recursive to String uses too soon... true? + // Yes, the issue is that outer scope may create vars, the inner then does + // not have access! + default String render() { + return Renderer.render(this); + } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestParameters.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestArguments.java similarity index 74% rename from test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestParameters.java rename to test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestArguments.java index f5a22a0123311..0833bc584878c 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestParameters.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestArguments.java @@ -23,20 +23,19 @@ /* * @test - * @summary Example test with parameters. + * @summary Test templates with arguments. * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run driver template_framework.examples.TestParameters + * @run driver template_framework.examples.TestArguments */ package template_framework.examples; import compiler.lib.compile_framework.*; -import compiler.lib.template_framework.*; -import static compiler.lib.template_framework.Renderer.*; -import static compiler.lib.template_framework.StaticHelpers.*; +import compiler.lib.template_framework.Template; +import static compiler.lib.template_framework.Template.body; -public class TestParameters { +public class TestArguments { public static void main(String[] args) { // Create a new CompileFramework instance. @@ -52,7 +51,7 @@ public static void main(String[] args) { Object ret = comp.invoke("p.xyz.InnerTest", "test", new Object[] {}); System.out.println("res: " + ret); - // Check that the return value is the sum of the two parameters. + // Check that the return value is the sum of the two arguments. if ((42 + 7) != (int)ret) { throw new RuntimeException("Unexpected result"); } @@ -60,19 +59,19 @@ public static void main(String[] args) { // Generate a source Java file as String public static String generate() { - // Create a Template with two parameter holes. - var template = Template.make("param1", "param2", (String param1, String param2) -> body( + // Create a Template with two arguments. + var template = Template.make("arg1", "arg2", (String arg1, String arg2) -> body( """ package p.xyz; public class InnerTest { public static int test() { - return #param1 + #param2; + return #arg1 + #arg2; } } """ )); - // The two parameter holes are to be replaced with the provided values. - return Renderer.render(template.withArgs("42", "7")); + // Use the template with two arguments, and render it to a String. + return template.withArgs("42", "7").render(); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java new file mode 100644 index 0000000000000..1830c01e9154c --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -0,0 +1,81 @@ +/* + * 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 + * @summary Test some basic Template instantiations. We do not necessarily generate correct + * java code, we just test that the code generation deterministically creates the + * expected String. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver template_framework.tests.TestTemplate + */ + +package template_framework.tests; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Random; + +import jdk.test.lib.Utils; + +import compiler.lib.template_framework.*; +import compiler.lib.template_framework.Template; +import static compiler.lib.template_framework.Template.body; + +public class TestTemplate { + private static final Random RANDOM = Utils.getRandomInstance(); + + public static void main(String[] args) { + testSingleLine(); + //testMultiLine(); + //testMultiLineWithParameters(); + //testCustomLibrary(); + //testClassInstantiator(); + //testRepeat(); + //testDispatch(); + //testClassInstantiatorAndDispatch(); + //testChoose(); + //testFieldsAndVariables(); + //testFieldsAndVariablesDispatch(); + //testIntCon(); + //testLongCon(); + //testFuel(); + //testRecursiveCalls(); + } + + public static void testSingleLine() { + var template = Template.make(() -> body("Hello World!")); + String code = template.withArgs().render(); + checkEQ(code, "Hello World!"); + } + + + 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 c93c1934d99e186801dd9d453fe3c964389f74bb Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 3 Feb 2025 17:09:59 +0100 Subject: [PATCH 004/212] more basic tests --- .../tests/TestTemplate.java | 87 ++++++++++++++++++- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 1830c01e9154c..a01937ef27817 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -48,8 +48,10 @@ public class TestTemplate { public static void main(String[] args) { testSingleLine(); - //testMultiLine(); - //testMultiLineWithParameters(); + testMultiLine(); + testBodyElements(); + testWithOneArguments(); + testWithTwoArguments(); //testCustomLibrary(); //testClassInstantiator(); //testRepeat(); @@ -70,6 +72,87 @@ public static void testSingleLine() { checkEQ(code, "Hello World!"); } + public static void testMultiLine() { + var template = Template.make(() -> body( + """ + Code on more + than a single line + """ + )); + String code = template.withArgs().render(); + String expected = + """ + Code on more + than a single line + """; + checkEQ(code, expected); + } + + public static void testBodyElements() { + // We can fill the body with Objects of different types, and they get concatenated. + var template = Template.make(() -> body( + "start ", + new Integer(1), + new Long(2), + new Double(3.4), + new Float(5.6), + " end" + )); + String code = template.withArgs().render(); + checkEQ(code, "start 123.45.6 end"); + } + + public static void testWithOneArguments() { + // Capture String argument via String name. + var template1 = Template.make("p", (String p) -> body("start #p end")); + checkEQ(template1.withArgs("x").render(), "start x end"); + checkEQ(template1.withArgs("a").render(), "start a end"); + checkEQ(template1.withArgs("" ).render(), "start end"); + + // Capture String argument via typed lambda argument. + var template2 = Template.make("p", (String p) -> body("start ", p, " end")); + checkEQ(template2.withArgs("x").render(), "start x end"); + checkEQ(template2.withArgs("a").render(), "start a end"); + checkEQ(template2.withArgs("" ).render(), "start end"); + + // Capture Integer argument via String name. + var template3 = Template.make("p", (Integer p) -> body("start #p end")); + checkEQ(template3.withArgs(0 ).render(), "start 0 end"); + checkEQ(template3.withArgs(22 ).render(), "start 22 end"); + checkEQ(template3.withArgs(444).render(), "start 444 end"); + + // Capture Integer argument via templated lambda argument. + var template4 = Template.make("p", (Integer p) -> body("start ", p, " end")); + checkEQ(template4.withArgs(0 ).render(), "start 0 end"); + checkEQ(template4.withArgs(22 ).render(), "start 22 end"); + checkEQ(template4.withArgs(444).render(), "start 444 end"); + } + + public static void testWithTwoArguments() { + // Capture 2 String arguments via String names. + var template1 = Template.make("p1", "p2", (String p1, String p2) -> body("start #p1 #p2 end")); + checkEQ(template1.withArgs("x", "y").render(), "start x y end"); + checkEQ(template1.withArgs("a", "b").render(), "start a b end"); + checkEQ(template1.withArgs("", "" ).render(), "start end"); + + // Capture 2 String arguments via typed lambda arguments. + var template2 = Template.make("p1", "p2", (String p1, String p2) -> body("start ", p1, " ", p2, " end")); + checkEQ(template2.withArgs("x", "y").render(), "start x y end"); + checkEQ(template2.withArgs("a", "b").render(), "start a b end"); + checkEQ(template2.withArgs("", "" ).render(), "start end"); + + // Capture 2 Integer arguments via String names. + var template3 = Template.make("p1", "p2", (Integer p1, Integer p2) -> body("start #p1 #p2 end")); + checkEQ(template3.withArgs(0, 1 ).render(), "start 0 1 end"); + checkEQ(template3.withArgs(22, 33 ).render(), "start 22 33 end"); + checkEQ(template3.withArgs(444, 555).render(), "start 444 555 end"); + + // Capture 2 Integer arguments via templated lambda arguments. + var template4 = Template.make("p1", "p2", (Integer p1, Integer p2) -> body("start ", p1, " ", p2, " end")); + checkEQ(template4.withArgs(0, 1 ).render(), "start 0 1 end"); + checkEQ(template4.withArgs(22, 33 ).render(), "start 22 33 end"); + checkEQ(template4.withArgs(444, 555).render(), "start 444 555 end"); + } public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { From 6a4d875125d11cf840197a38837bf1e5f7c3caad Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 3 Feb 2025 17:22:21 +0100 Subject: [PATCH 005/212] test wip --- .../tests/TestTemplate.java | 74 ++++++++++++++++--- 1 file changed, 65 insertions(+), 9 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index a01937ef27817..9a9cc9713815d 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -52,7 +52,7 @@ public static void main(String[] args) { testBodyElements(); testWithOneArguments(); testWithTwoArguments(); - //testCustomLibrary(); + testCustomLibrary(); //testClassInstantiator(); //testRepeat(); //testDispatch(); @@ -104,25 +104,25 @@ public static void testBodyElements() { public static void testWithOneArguments() { // Capture String argument via String name. - var template1 = Template.make("p", (String p) -> body("start #p end")); + var template1 = Template.make("a", (String a) -> body("start #a end")); checkEQ(template1.withArgs("x").render(), "start x end"); checkEQ(template1.withArgs("a").render(), "start a end"); checkEQ(template1.withArgs("" ).render(), "start end"); // Capture String argument via typed lambda argument. - var template2 = Template.make("p", (String p) -> body("start ", p, " end")); + var template2 = Template.make("a", (String a) -> body("start ", a, " end")); checkEQ(template2.withArgs("x").render(), "start x end"); checkEQ(template2.withArgs("a").render(), "start a end"); checkEQ(template2.withArgs("" ).render(), "start end"); // Capture Integer argument via String name. - var template3 = Template.make("p", (Integer p) -> body("start #p end")); + var template3 = Template.make("a", (Integer a) -> body("start #a end")); checkEQ(template3.withArgs(0 ).render(), "start 0 end"); checkEQ(template3.withArgs(22 ).render(), "start 22 end"); checkEQ(template3.withArgs(444).render(), "start 444 end"); // Capture Integer argument via templated lambda argument. - var template4 = Template.make("p", (Integer p) -> body("start ", p, " end")); + var template4 = Template.make("a", (Integer a) -> body("start ", a, " end")); checkEQ(template4.withArgs(0 ).render(), "start 0 end"); checkEQ(template4.withArgs(22 ).render(), "start 22 end"); checkEQ(template4.withArgs(444).render(), "start 444 end"); @@ -130,30 +130,86 @@ public static void testWithOneArguments() { public static void testWithTwoArguments() { // Capture 2 String arguments via String names. - var template1 = Template.make("p1", "p2", (String p1, String p2) -> body("start #p1 #p2 end")); + var template1 = Template.make("a1", "a2", (String a1, String a2) -> body("start #a1 #a2 end")); checkEQ(template1.withArgs("x", "y").render(), "start x y end"); checkEQ(template1.withArgs("a", "b").render(), "start a b end"); checkEQ(template1.withArgs("", "" ).render(), "start end"); // Capture 2 String arguments via typed lambda arguments. - var template2 = Template.make("p1", "p2", (String p1, String p2) -> body("start ", p1, " ", p2, " end")); + var template2 = Template.make("a1", "a2", (String a1, String a2) -> body("start ", a1, " ", a2, " end")); checkEQ(template2.withArgs("x", "y").render(), "start x y end"); checkEQ(template2.withArgs("a", "b").render(), "start a b end"); checkEQ(template2.withArgs("", "" ).render(), "start end"); // Capture 2 Integer arguments via String names. - var template3 = Template.make("p1", "p2", (Integer p1, Integer p2) -> body("start #p1 #p2 end")); + var template3 = Template.make("a1", "a2", (Integer a1, Integer a2) -> body("start #a1 #a2 end")); checkEQ(template3.withArgs(0, 1 ).render(), "start 0 1 end"); checkEQ(template3.withArgs(22, 33 ).render(), "start 22 33 end"); checkEQ(template3.withArgs(444, 555).render(), "start 444 555 end"); // Capture 2 Integer arguments via templated lambda arguments. - var template4 = Template.make("p1", "p2", (Integer p1, Integer p2) -> body("start ", p1, " ", p2, " end")); + var template4 = Template.make("a1", "a2", (Integer a1, Integer a2) -> body("start ", a1, " ", a2, " end")); checkEQ(template4.withArgs(0, 1 ).render(), "start 0 1 end"); checkEQ(template4.withArgs(22, 33 ).render(), "start 22 33 end"); checkEQ(template4.withArgs(444, 555).render(), "start 444 555 end"); } + public static void testCustomLibrary() { + var template1 = Template.make(() -> body("proton")); + + var template2 = Template.make("a1", "a2", (String a1, String a2) -> body( + """ + electron #a1 + neutron #a2 + """ + )); + + //var template3 = Template.make(() -> body( + // """ + // Universe """, template.withArgs(), """ { + // #{:my_generator_2(param1=up,param2=down)} + // #{:my_generator_2(param1=#param1,param2=#param2)} + // } + // """ + //)); + //CodeGeneratorLibrary library = new CodeGeneratorLibrary(null, codeGenerators); + + //Template template = new Template("my_template", + // """ + // #{:my_generator_3(param1=low,param2=high)} + // { + // #{:my_generator_3(param1=42,param2=24)} + // } + // """ + //); + + //String code = template.with(library).instantiate(); + //String expected = + // """ + // Universe proton { + // electron up + // neutron down + + // electron low + // neutron high + + // } + + // { + // Universe proton { + // electron up + // neutron down + + // electron 42 + // neutron 24 + + // } + + // } + // """; + //checkEQ(code, expected); + } + public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { System.out.println("\"" + code + "\""); From 09b41a1bbbc010f8e4b25760b7f90f4003dbf064 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 3 Feb 2025 18:21:38 +0100 Subject: [PATCH 006/212] more test wip --- .../tests/TestTemplate.java | 94 ++++++++----------- 1 file changed, 40 insertions(+), 54 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 9a9cc9713815d..5cf21237366d5 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -52,7 +52,7 @@ public static void main(String[] args) { testBodyElements(); testWithOneArguments(); testWithTwoArguments(); - testCustomLibrary(); + testRecursive(); //testClassInstantiator(); //testRepeat(); //testDispatch(); @@ -92,10 +92,10 @@ public static void testBodyElements() { // We can fill the body with Objects of different types, and they get concatenated. var template = Template.make(() -> body( "start ", - new Integer(1), - new Long(2), - new Double(3.4), - new Float(5.6), + Integer.valueOf(1), + Long.valueOf(2), + Double.valueOf(3.4), + Float.valueOf(5.6f), " end" )); String code = template.withArgs().render(); @@ -154,60 +154,46 @@ public static void testWithTwoArguments() { checkEQ(template4.withArgs(444, 555).render(), "start 444 555 end"); } - public static void testCustomLibrary() { + public static void testRecursive() { var template1 = Template.make(() -> body("proton")); var template2 = Template.make("a1", "a2", (String a1, String a2) -> body( - """ - electron #a1 - neutron #a2 - """ + "electron #a1\n", + "neutron #a2\n" )); - //var template3 = Template.make(() -> body( - // """ - // Universe """, template.withArgs(), """ { - // #{:my_generator_2(param1=up,param2=down)} - // #{:my_generator_2(param1=#param1,param2=#param2)} - // } - // """ - //)); - //CodeGeneratorLibrary library = new CodeGeneratorLibrary(null, codeGenerators); - - //Template template = new Template("my_template", - // """ - // #{:my_generator_3(param1=low,param2=high)} - // { - // #{:my_generator_3(param1=42,param2=24)} - // } - // """ - //); - - //String code = template.with(library).instantiate(); - //String expected = - // """ - // Universe proton { - // electron up - // neutron down - - // electron low - // neutron high - - // } - - // { - // Universe proton { - // electron up - // neutron down - - // electron 42 - // neutron 24 - - // } - - // } - // """; - //checkEQ(code, expected); + var template3 = Template.make("a1", "a2", (String a1, String a2) -> body( + "Universe ", template1.withArgs(), " {\n", + template2.withArgs("up", "down"), + template2.withArgs(a1, a2), + "}\n" + )); + + var template4 = Template.make(() -> body( + template3.withArgs("low", "high"), + "{\n", + template3.withArgs("42", "24"), + "}" + )); + + String code = template4.withArgs().render(); + String expected = + """ + Universe proton { + electron up + neutron down + electron low + neutron high + } + { + Universe proton { + electron up + neutron down + electron 42 + neutron 24 + } + }"""; + checkEQ(code, expected); } public static void checkEQ(String code, String expected) { From cea77c4c044c4a08f1edf6e0f369bb197c865d65 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 3 Feb 2025 21:04:31 +0100 Subject: [PATCH 007/212] refactor and handle List --- .../lib/template_framework/Renderer.java | 68 +++++++++++-------- .../tests/TestTemplate.java | 7 +- 2 files changed, 46 insertions(+), 29 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index cca005f811bbc..83f2acac10b75 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -37,14 +37,17 @@ public static final class Nothing { private Nothing() {} } - private static final Pattern variableNamePattern = Pattern.compile("\\$([a-zA-Z_][a-zA-Z0-9_]*)"); - private static final Pattern interpolationPattern = Pattern.compile("#([a-zA-Z_][a-zA-Z0-9_]*)"); - private static final ArrayList stack = new ArrayList(); + private static final Pattern DOLLAR_NAME_PATTERN = Pattern.compile("\\$([a-zA-Z_][a-zA-Z0-9_]*)"); + private static final Pattern HASHTAG_REPLACEMENT_PATTERN = Pattern.compile("#([a-zA-Z_][a-zA-Z0-9_]*)"); + private static final ArrayList STACK = new ArrayList(); static int variableId = 0; public static String render(TemplateUse templateUse) { - return visit(templateUse); + if (!STACK.isEmpty()) { + throw new RendererException("Nested render not allowed"); + } + return renderTemplateUse(templateUse); } public static String $(String name) { @@ -62,48 +65,57 @@ public static R let(String key, T value, Function block) { } public static int depth() { - return stack.size(); + return STACK.size(); } private static Frame currentStackFrame() { - if (stack.isEmpty()) { + if (STACK.isEmpty()) { throw new RendererException("A method such as $ or let was called outside a template rendering. Make sure you are not calling templates yourself, but use use()."); } - return stack.getLast(); + return STACK.getLast(); } - private static String visit(TemplateUse templateUse) { + private static String renderTemplateUse(TemplateUse templateUse) { Frame frame = new Frame(); - stack.add(frame); + STACK.add(frame); templateUse.visitArguments((name, value) -> frame.addContext(name, value.toString())); InstantiatedTemplate it = templateUse.instantiate(); - for (Object i : it.elements()) { - switch (i) { - case Nothing x -> {} - case String s -> frame.addString(templateString(s, frame)); - case Integer s -> frame.addString(s.toString()); - case Long s -> frame.addString(s.toString()); - case Double s -> frame.addString(s.toString()); - case Float s -> frame.addString(s.toString()); - case Hook h -> frame.addHook(h); - case HookInsert h -> - frameForHook(h.hook()).insertIntoHook(h.hook(), visit(h.templateUse())); - case TemplateUse t -> frame.addString(visit(t)); - default -> throw new RendererException("body contained unexpected element: " + i); - } + for (Object e : it.elements()) { + renderElement(frame, e); } - stack.removeLast(); + STACK.removeLast(); return frame.toString(); } + private static void renderElement(Frame frame, Object element) { + switch (element) { + case Nothing x -> {} + case String s -> frame.addString(templateString(s, frame)); + case Integer s -> frame.addString(s.toString()); + case Long s -> frame.addString(s.toString()); + case Double s -> frame.addString(s.toString()); + case Float s -> frame.addString(s.toString()); + case Hook h -> frame.addHook(h); + case List l -> { + for (Object e : l) { + renderElement(frame, e); + } + } + case HookInsert h -> + frameForHook(h.hook()).insertIntoHook(h.hook(), render(h.templateUse())); + case TemplateUse t -> frame.addString(renderTemplateUse(t)); + default -> throw new RendererException("body contained unexpected element: " + element); + } + } + private static String templateString(String s, Frame frame) { - var temp = variableNamePattern.matcher(s).replaceAll((MatchResult result) -> $(result.group(1))); - return interpolationPattern.matcher(temp).replaceAll((MatchResult result) -> frame.getContext(result.group(1))); + var temp = DOLLAR_NAME_PATTERN.matcher(s).replaceAll((MatchResult result) -> $(result.group(1))); + return HASHTAG_REPLACEMENT_PATTERN.matcher(temp).replaceAll((MatchResult result) -> frame.getContext(result.group(1))); } private static Frame frameForHook(Hook hook) { - for (int i = stack.size() - 1; i >= 0; i--) { - Frame frame = stack.get(i); + for (int i = STACK.size() - 1; i >= 0; i--) { + Frame frame = STACK.get(i); if (frame.hasHook(hook)) { return frame; } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 5cf21237366d5..19461ccde4be5 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -34,6 +34,7 @@ package template_framework.tests; import java.util.Arrays; +import java.util.List; import java.util.HashSet; import java.util.Random; @@ -90,16 +91,18 @@ public static void testMultiLine() { public static void testBodyElements() { // We can fill the body with Objects of different types, and they get concatenated. + // Lists get flattened into the body. var template = Template.make(() -> body( "start ", Integer.valueOf(1), Long.valueOf(2), Double.valueOf(3.4), Float.valueOf(5.6f), + List.of(" ", 1, " and ", 2), " end" )); String code = template.withArgs().render(); - checkEQ(code, "start 123.45.6 end"); + checkEQ(code, "start 123.45.6 1 and 2 end"); } public static void testWithOneArguments() { @@ -196,6 +199,8 @@ public static void testRecursive() { checkEQ(code, expected); } + + public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { System.out.println("\"" + code + "\""); From 2af160953a66bf5f6961d1ced1568d2a09a93d02 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 3 Feb 2025 21:28:31 +0100 Subject: [PATCH 008/212] wip Code render refactor wip --- .../lib/template_framework/Frame.java | 49 +++++++++++++------ .../lib/template_framework/Renderer.java | 26 +++++----- 2 files changed, 46 insertions(+), 29 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java index d86882e4fb1c4..7f01b68dbe43d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java @@ -25,30 +25,37 @@ import java.util.HashMap; import java.util.Map; +import java.util.ArrayList; +import java.util.List; class Frame { - private final StringBuilder builder = new StringBuilder(); - private final Map hookInsertionIndex = new HashMap<>(); + sealed interface Code permits Token, CodeList {} + record Token(String s) implements Code {} + record CodeList(List list) implements Code {} + + private final List codeList = new ArrayList(); + + //private final Map hookInsertionIndex = new HashMap<>(); private final Map variableNames = new HashMap<>(); private final Map context = new HashMap<>(); void addString(String s) { - builder.append(s); + codeList.add(new Token(s)); } - void addHook(Hook hook) { - hookInsertionIndex.put(hook, builder.length()); - } + //void addHook(Hook hook) { + // hookInsertionIndex.put(hook, builder.length()); + //} - boolean hasHook(Hook hook) { - return hookInsertionIndex.containsKey(hook); - } + //boolean hasHook(Hook hook) { + // return hookInsertionIndex.containsKey(hook); + //} - void insertIntoHook(Hook hook, String s) { - int index = hookInsertionIndex.get(hook); - builder.insert(index, s); - hookInsertionIndex.put(hook, index + s.length()); - } + //void insertIntoHook(Hook hook, String s) { + // int index = hookInsertionIndex.get(hook); + // builder.insert(index, s); + // hookInsertionIndex.put(hook, index + s.length()); + //} public void addContext(String key, String value) { context.put(key, value); @@ -65,8 +72,18 @@ String variableName(String name) { return variableNames.computeIfAbsent(name, s -> name + Renderer.variableId++); } - @Override - public String toString() { + String render() { + StringBuilder builder = new StringBuilder(); + for (Code code : codeList) { + renderCode(builder, code); + } return builder.toString(); } + + void renderCode(StringBuilder builder, Code code) { + switch (code) { + case Token(String s) -> builder.append(s); + case CodeList(List list) -> list.forEach((Code c) -> renderCode(builder, c)); + } + } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 83f2acac10b75..ef176d17f5848 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -84,7 +84,7 @@ private static String renderTemplateUse(TemplateUse templateUse) { renderElement(frame, e); } STACK.removeLast(); - return frame.toString(); + return frame.render(); } private static void renderElement(Frame frame, Object element) { @@ -95,14 +95,14 @@ private static void renderElement(Frame frame, Object element) { case Long s -> frame.addString(s.toString()); case Double s -> frame.addString(s.toString()); case Float s -> frame.addString(s.toString()); - case Hook h -> frame.addHook(h); case List l -> { for (Object e : l) { renderElement(frame, e); } } - case HookInsert h -> - frameForHook(h.hook()).insertIntoHook(h.hook(), render(h.templateUse())); + //case Hook h -> frame.addHook(h); + //case HookInsert h -> + // frameForHook(h.hook()).insertIntoHook(h.hook(), render(h.templateUse())); case TemplateUse t -> frame.addString(renderTemplateUse(t)); default -> throw new RendererException("body contained unexpected element: " + element); } @@ -113,13 +113,13 @@ private static String templateString(String s, Frame frame) { return HASHTAG_REPLACEMENT_PATTERN.matcher(temp).replaceAll((MatchResult result) -> frame.getContext(result.group(1))); } - private static Frame frameForHook(Hook hook) { - for (int i = STACK.size() - 1; i >= 0; i--) { - Frame frame = STACK.get(i); - if (frame.hasHook(hook)) { - return frame; - } - } - throw new RuntimeException("hook " + hook.name() + " was referenced but not found!"); - } + //private static Frame frameForHook(Hook hook) { + // for (int i = STACK.size() - 1; i >= 0; i--) { + // Frame frame = STACK.get(i); + // if (frame.hasHook(hook)) { + // return frame; + // } + // } + // throw new RuntimeException("hook " + hook.name() + " was referenced but not found!"); + //} } From 4e1fd4b0dbfe56c772616b148c39d46d0f009734 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 3 Feb 2025 21:57:49 +0100 Subject: [PATCH 009/212] nested code rendering --- .../compiler/lib/template_framework/Code.java | 46 +++++++++++++++++++ .../lib/template_framework/Frame.java | 26 ++++------- .../lib/template_framework/Renderer.java | 10 ++-- 3 files changed, 60 insertions(+), 22 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/Code.java diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Code.java b/test/hotspot/jtreg/compiler/lib/template_framework/Code.java new file mode 100644 index 0000000000000..8b3b8387e680b --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Code.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; + +import java.util.ArrayList; +import java.util.List; + +sealed interface Code permits Code.Token, Code.CodeList { + + record Token(String s) implements Code { + @Override + public void renderTo(StringBuilder builder) { + builder.append(s); + } + } + + record CodeList(List list) implements Code { + @Override + public void renderTo(StringBuilder builder) { + list.forEach(code -> code.renderTo(builder)); + } + } + + void renderTo(StringBuilder builder); +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java index 7f01b68dbe43d..98b246cb607a6 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java @@ -29,10 +29,6 @@ import java.util.List; class Frame { - sealed interface Code permits Token, CodeList {} - record Token(String s) implements Code {} - record CodeList(List list) implements Code {} - private final List codeList = new ArrayList(); //private final Map hookInsertionIndex = new HashMap<>(); @@ -40,7 +36,11 @@ record CodeList(List list) implements Code {} private final Map context = new HashMap<>(); void addString(String s) { - codeList.add(new Token(s)); + codeList.add(new Code.Token(s)); + } + + void addCode(Code code) { + codeList.add(code); } //void addHook(Hook hook) { @@ -72,18 +72,8 @@ String variableName(String name) { return variableNames.computeIfAbsent(name, s -> name + Renderer.variableId++); } - String render() { - StringBuilder builder = new StringBuilder(); - for (Code code : codeList) { - renderCode(builder, code); - } - return builder.toString(); - } - - void renderCode(StringBuilder builder, Code code) { - switch (code) { - case Token(String s) -> builder.append(s); - case CodeList(List list) -> list.forEach((Code c) -> renderCode(builder, c)); - } + // TODO ensure only use once! + Code getCode() { + return new Code.CodeList(codeList); } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index ef176d17f5848..d9813b3ce6adf 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -47,7 +47,9 @@ public static String render(TemplateUse templateUse) { if (!STACK.isEmpty()) { throw new RendererException("Nested render not allowed"); } - return renderTemplateUse(templateUse); + StringBuilder builder = new StringBuilder(); + renderTemplateUse(templateUse).renderTo(builder); + return builder.toString(); } public static String $(String name) { @@ -75,7 +77,7 @@ private static Frame currentStackFrame() { return STACK.getLast(); } - private static String renderTemplateUse(TemplateUse templateUse) { + private static Code renderTemplateUse(TemplateUse templateUse) { Frame frame = new Frame(); STACK.add(frame); templateUse.visitArguments((name, value) -> frame.addContext(name, value.toString())); @@ -84,7 +86,7 @@ private static String renderTemplateUse(TemplateUse templateUse) { renderElement(frame, e); } STACK.removeLast(); - return frame.render(); + return frame.getCode(); } private static void renderElement(Frame frame, Object element) { @@ -103,7 +105,7 @@ private static void renderElement(Frame frame, Object element) { //case Hook h -> frame.addHook(h); //case HookInsert h -> // frameForHook(h.hook()).insertIntoHook(h.hook(), render(h.templateUse())); - case TemplateUse t -> frame.addString(renderTemplateUse(t)); + case TemplateUse t -> frame.addCode(renderTemplateUse(t)); default -> throw new RendererException("body contained unexpected element: " + element); } } From 7be8772e36d82499fb3722a2de020c705c7f45b3 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 4 Feb 2025 08:58:15 +0100 Subject: [PATCH 010/212] setting and findig hooks --- .../lib/template_framework/Frame.java | 17 +++++++------ .../lib/template_framework/Renderer.java | 25 ++++++++++--------- .../tests/TestTemplate.java | 21 ++++++++++++++++ 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java index 98b246cb607a6..e8b913c82833d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java @@ -30,8 +30,8 @@ class Frame { private final List codeList = new ArrayList(); + private final Map hookCodeLists = new HashMap<>(); - //private final Map hookInsertionIndex = new HashMap<>(); private final Map variableNames = new HashMap<>(); private final Map context = new HashMap<>(); @@ -43,13 +43,16 @@ void addCode(Code code) { codeList.add(code); } - //void addHook(Hook hook) { - // hookInsertionIndex.put(hook, builder.length()); - //} + void addHook(Hook hook) { + if (hasHook(hook)) { + throw new RendererException("Duplicate Hook in Template: " + hook.name()); + } + hookCodeLists.put(hook, new Code.CodeList(new ArrayList())); + } - //boolean hasHook(Hook hook) { - // return hookInsertionIndex.containsKey(hook); - //} + boolean hasHook(Hook hook) { + return hookCodeLists.containsKey(hook); + } //void insertIntoHook(Hook hook, String s) { // int index = hookInsertionIndex.get(hook); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index d9813b3ce6adf..f3bba41e761a0 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -102,9 +102,10 @@ private static void renderElement(Frame frame, Object element) { renderElement(frame, e); } } - //case Hook h -> frame.addHook(h); - //case HookInsert h -> - // frameForHook(h.hook()).insertIntoHook(h.hook(), render(h.templateUse())); + case Hook h -> frame.addHook(h); + case HookInsert(Hook hook, TemplateUse t) -> { + Frame f = frameForHook(hook); //.insertIntoHook(hook, render(t)); + } case TemplateUse t -> frame.addCode(renderTemplateUse(t)); default -> throw new RendererException("body contained unexpected element: " + element); } @@ -115,13 +116,13 @@ private static String templateString(String s, Frame frame) { return HASHTAG_REPLACEMENT_PATTERN.matcher(temp).replaceAll((MatchResult result) -> frame.getContext(result.group(1))); } - //private static Frame frameForHook(Hook hook) { - // for (int i = STACK.size() - 1; i >= 0; i--) { - // Frame frame = STACK.get(i); - // if (frame.hasHook(hook)) { - // return frame; - // } - // } - // throw new RuntimeException("hook " + hook.name() + " was referenced but not found!"); - //} + private static Frame frameForHook(Hook hook) { + for (int i = STACK.size() - 1; i >= 0; i--) { + Frame frame = STACK.get(i); + if (frame.hasHook(hook)) { + return frame; + } + } + throw new RendererException("hook " + hook.name() + " was referenced but not found!"); + } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 19461ccde4be5..7a5c72f249eca 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -43,6 +43,7 @@ import compiler.lib.template_framework.*; import compiler.lib.template_framework.Template; import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.intoHook; public class TestTemplate { private static final Random RANDOM = Utils.getRandomInstance(); @@ -54,6 +55,8 @@ public static void main(String[] args) { testWithOneArguments(); testWithTwoArguments(); testRecursive(); + testHook(); + //testClassInstantiator(); //testRepeat(); //testDispatch(); @@ -199,7 +202,25 @@ public static void testRecursive() { checkEQ(code, expected); } + public static void testHook() { + var hook1 = new Hook("Hook1"); + + var template1 = Template.make(() -> body("Hello\n")); + + var template2 = Template.make(() -> body( + "{\n", + hook1, + "World\n", + intoHook(hook1, template1.withArgs()), + "}" + )); + String code = template2.withArgs().render(); + String expected = + """ + xxx"""; + checkEQ(code, expected); + } public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { From 34522906a014a671085dd4361db6d724f559852a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 4 Feb 2025 09:38:10 +0100 Subject: [PATCH 011/212] STACK -> Frame.parent --- .../lib/template_framework/Frame.java | 10 +++ .../lib/template_framework/Renderer.java | 80 +++++++++++++------ 2 files changed, 66 insertions(+), 24 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java index e8b913c82833d..526036df89233 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java @@ -29,12 +29,22 @@ import java.util.List; class Frame { + public final Frame parent; private final List codeList = new ArrayList(); private final Map hookCodeLists = new HashMap<>(); private final Map variableNames = new HashMap<>(); private final Map context = new HashMap<>(); + Frame(Frame parent) { + this.parent = parent; + } + + public int depth() { + if (parent == null) { return 0; } + return parent.depth() + 1; + } + void addString(String s) { codeList.add(new Code.Token(s)); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index f3bba41e761a0..d523c6a1590b9 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -39,57 +39,88 @@ private Nothing() {} private static final Pattern DOLLAR_NAME_PATTERN = Pattern.compile("\\$([a-zA-Z_][a-zA-Z0-9_]*)"); private static final Pattern HASHTAG_REPLACEMENT_PATTERN = Pattern.compile("#([a-zA-Z_][a-zA-Z0-9_]*)"); - private static final ArrayList STACK = new ArrayList(); + private static Frame baseFrame = null; + private static Frame currentFrame = null; static int variableId = 0; public static String render(TemplateUse templateUse) { - if (!STACK.isEmpty()) { - throw new RendererException("Nested render not allowed"); + // Check nobody else is using the Renderer. + if (baseFrame != null) { + throw new RendererException("Nested render not allowed."); } + baseFrame = new Frame(null); + currentFrame = baseFrame; + + renderTemplateUse(templateUse); + + // Ensure Frame consistency. + if (baseFrame != currentFrame) { + throw new RendererException("Renderer did not end up at base frame."); + } + + // Collect Code to String. StringBuilder builder = new StringBuilder(); - renderTemplateUse(templateUse).renderTo(builder); - return builder.toString(); + baseFrame.getCode().renderTo(builder); + String code = builder.toString(); + + // Release the Renderer. + baseFrame = null; + + return code; } public static String $(String name) { - return currentStackFrame().variableName(name); + return getCurrentFrame().variableName(name); } public static Nothing let(String key, Object value) { - currentStackFrame().addContext(key, value.toString()); + getCurrentFrame().addContext(key, value.toString()); return Nothing.instance; } public static R let(String key, T value, Function block) { - currentStackFrame().addContext(key, value.toString()); + getCurrentFrame().addContext(key, value.toString()); return block.apply(value); } + // TODO fuel public static int depth() { - return STACK.size(); + return getCurrentFrame().depth(); } - private static Frame currentStackFrame() { - if (STACK.isEmpty()) { + private static Frame getCurrentFrame() { + if (currentFrame == null) { + // TODO update text throw new RendererException("A method such as $ or let was called outside a template rendering. Make sure you are not calling templates yourself, but use use()."); } - return STACK.getLast(); + return currentFrame; } - private static Code renderTemplateUse(TemplateUse templateUse) { - Frame frame = new Frame(); - STACK.add(frame); - templateUse.visitArguments((name, value) -> frame.addContext(name, value.toString())); + private static void renderTemplateUse(TemplateUse templateUse) { + // Add nested Frame. + Frame frame = new Frame(getCurrentFrame()); + currentFrame = frame; + + templateUse.visitArguments((name, value) -> currentFrame.addContext(name, value.toString())); InstantiatedTemplate it = templateUse.instantiate(); for (Object e : it.elements()) { - renderElement(frame, e); + renderElement(e); + } + + if (frame != getCurrentFrame()) { + throw new RendererException("Frame mismatch."); } - STACK.removeLast(); - return frame.getCode(); + + // Append code from nested scope to outer scope. + frame.parent.addCode(frame.getCode()); + + // Remove nested frame. + currentFrame = currentFrame.parent; } - private static void renderElement(Frame frame, Object element) { + private static void renderElement(Object element) { + Frame frame = getCurrentFrame(); switch (element) { case Nothing x -> {} case String s -> frame.addString(templateString(s, frame)); @@ -99,14 +130,14 @@ private static void renderElement(Frame frame, Object element) { case Float s -> frame.addString(s.toString()); case List l -> { for (Object e : l) { - renderElement(frame, e); + renderElement(e); } } case Hook h -> frame.addHook(h); case HookInsert(Hook hook, TemplateUse t) -> { Frame f = frameForHook(hook); //.insertIntoHook(hook, render(t)); } - case TemplateUse t -> frame.addCode(renderTemplateUse(t)); + case TemplateUse t -> renderTemplateUse(t); default -> throw new RendererException("body contained unexpected element: " + element); } } @@ -117,11 +148,12 @@ private static String templateString(String s, Frame frame) { } private static Frame frameForHook(Hook hook) { - for (int i = STACK.size() - 1; i >= 0; i--) { - Frame frame = STACK.get(i); + Frame frame = getCurrentFrame(); + while (frame != null) { if (frame.hasHook(hook)) { return frame; } + frame = frame.parent; } throw new RendererException("hook " + hook.name() + " was referenced but not found!"); } From e7072040a55f0acb15aec4524f7b6734673ae5ac Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 4 Feb 2025 10:22:13 +0100 Subject: [PATCH 012/212] move frame creation to recursive template use --- .../lib/template_framework/Frame.java | 6 ---- .../lib/template_framework/Renderer.java | 29 ++++++++++--------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java index 526036df89233..860efbf3ff386 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java @@ -64,12 +64,6 @@ boolean hasHook(Hook hook) { return hookCodeLists.containsKey(hook); } - //void insertIntoHook(Hook hook, String s) { - // int index = hookInsertionIndex.get(hook); - // builder.insert(index, s); - // hookInsertionIndex.put(hook, index + s.length()); - //} - public void addContext(String key, String value) { context.put(key, value); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index d523c6a1590b9..b8cd6c99a1c20 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -98,11 +98,9 @@ private static Frame getCurrentFrame() { } private static void renderTemplateUse(TemplateUse templateUse) { - // Add nested Frame. - Frame frame = new Frame(getCurrentFrame()); - currentFrame = frame; + Frame frame = getCurrentFrame(); - templateUse.visitArguments((name, value) -> currentFrame.addContext(name, value.toString())); + templateUse.visitArguments((name, value) -> frame.addContext(name, value.toString())); InstantiatedTemplate it = templateUse.instantiate(); for (Object e : it.elements()) { renderElement(e); @@ -111,12 +109,6 @@ private static void renderTemplateUse(TemplateUse templateUse) { if (frame != getCurrentFrame()) { throw new RendererException("Frame mismatch."); } - - // Append code from nested scope to outer scope. - frame.parent.addCode(frame.getCode()); - - // Remove nested frame. - currentFrame = currentFrame.parent; } private static void renderElement(Object element) { @@ -133,11 +125,22 @@ private static void renderElement(Object element) { renderElement(e); } } - case Hook h -> frame.addHook(h); + case Hook h -> { + frame.addHook(h); + } case HookInsert(Hook hook, TemplateUse t) -> { - Frame f = frameForHook(hook); //.insertIntoHook(hook, render(t)); + Frame hookFrame = frameForHook(hook); + currentFrame = hookFrame; // switch to hook frame. + renderTemplateUse(t); + currentFrame = frame; // switch back. + } + case TemplateUse t -> { + // Use a nested Frame. + currentFrame = new Frame(frame); + renderTemplateUse(t); + frame.addCode(currentFrame.getCode()); + currentFrame = frame; } - case TemplateUse t -> renderTemplateUse(t); default -> throw new RendererException("body contained unexpected element: " + element); } } From f8151d6fd391fd4de3908a28cea41da4ff854407 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 5 Feb 2025 16:59:23 +0100 Subject: [PATCH 013/212] implement HookUse --- .../compiler/lib/template_framework/Hook.java | 9 ++++- .../lib/template_framework/HookUse.java | 28 +++++++++++++ .../InstantiatedTemplate.java | 2 +- .../lib/template_framework/Renderer.java | 39 +++++++++++++++---- .../tests/TestTemplate.java | 10 +++-- 5 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/HookUse.java diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java index d56424fd6e98d..a7beb43ac72f0 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java @@ -23,4 +23,11 @@ package compiler.lib.template_framework; -public record Hook(String name) {} +import java.util.List; +import java.util.Arrays; + +public record Hook(String name) { + public HookUse set(Object... tokens) { + return new HookUse(this, Arrays.asList(tokens)); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/HookUse.java b/test/hotspot/jtreg/compiler/lib/template_framework/HookUse.java new file mode 100644 index 0000000000000..9ecde8f01dc10 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/HookUse.java @@ -0,0 +1,28 @@ +/* + * 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; + +import java.util.List; + +public record HookUse(Hook hook, List tokens) {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java b/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java index b0c1e1e29bbfa..59d1d85771541 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java @@ -23,4 +23,4 @@ package compiler.lib.template_framework; -public record InstantiatedTemplate(Object[] elements) {} +public record InstantiatedTemplate(Object[] tokens) {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index b8cd6c99a1c20..f9669805ec11e 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -102,8 +102,8 @@ private static void renderTemplateUse(TemplateUse templateUse) { templateUse.visitArguments((name, value) -> frame.addContext(name, value.toString())); InstantiatedTemplate it = templateUse.instantiate(); - for (Object e : it.elements()) { - renderElement(e); + for (Object token : it.tokens()) { + renderToken(token); } if (frame != getCurrentFrame()) { @@ -111,9 +111,9 @@ private static void renderTemplateUse(TemplateUse templateUse) { } } - private static void renderElement(Object element) { + private static void renderToken(Object token) { Frame frame = getCurrentFrame(); - switch (element) { + switch (token) { case Nothing x -> {} case String s -> frame.addString(templateString(s, frame)); case Integer s -> frame.addString(s.toString()); @@ -121,12 +121,35 @@ private static void renderElement(Object element) { case Double s -> frame.addString(s.toString()); case Float s -> frame.addString(s.toString()); case List l -> { - for (Object e : l) { - renderElement(e); + for (Object t : l) { + renderToken(t); } } case Hook h -> { - frame.addHook(h); + throw new RendererException("Do not use Hook directly, use Hook.set: " + h); + } + case HookUse(Hook hook, List tokens) -> { + Frame outerFrame = getCurrentFrame(); + + // We need a frame to which the hook can insert code. That way, name + // definitions at the hook cannot excape the hookFrame. + Frame hookFrame = new Frame(outerFrame); + hookFrame.addHook(hook); + + // We need a frame where the tokens can be rendered. That way, name + // definitions from the tokens cannot escape the innerFrame to the + // hookFrame. + Frame innerFrame = new Frame(hookFrame); + currentFrame = innerFrame; + // TODO render list, and verify frames are consistent. + for (Object t : tokens) { + renderToken(t); + } + // Close the hookFrame and innerFrame. hookFrame code comes before the + // innerFrame code from the tokens. + currentFrame = outerFrame; + currentFrame.addCode(hookFrame.getCode()); + currentFrame.addCode(innerFrame.getCode()); } case HookInsert(Hook hook, TemplateUse t) -> { Frame hookFrame = frameForHook(hook); @@ -141,7 +164,7 @@ case HookInsert(Hook hook, TemplateUse t) -> { frame.addCode(currentFrame.getCode()); currentFrame = frame; } - default -> throw new RendererException("body contained unexpected element: " + element); + default -> throw new RendererException("body contained unexpected token: " + token); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 7a5c72f249eca..0d95b718f08f8 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -209,16 +209,18 @@ public static void testHook() { var template2 = Template.make(() -> body( "{\n", - hook1, - "World\n", - intoHook(hook1, template1.withArgs()), + hook1.set( + "World\n", + intoHook(hook1, template1.withArgs()) + ), "}" )); String code = template2.withArgs().render(); String expected = """ - xxx"""; + Hello + World"""; checkEQ(code, expected); } From 6905e84cca337699e024e452ff830bea8661cc0a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 5 Feb 2025 17:18:30 +0100 Subject: [PATCH 014/212] nested hooks --- .../tests/TestTemplate.java | 70 ++++++++++++++++++- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 0d95b718f08f8..9bc7859afa3e4 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -55,7 +55,8 @@ public static void main(String[] args) { testWithOneArguments(); testWithTwoArguments(); testRecursive(); - testHook(); + testHook1(); + testHook2(); //testClassInstantiator(); //testRepeat(); @@ -202,7 +203,7 @@ public static void testRecursive() { checkEQ(code, expected); } - public static void testHook() { + public static void testHook1() { var hook1 = new Hook("Hook1"); var template1 = Template.make(() -> body("Hello\n")); @@ -219,8 +220,71 @@ public static void testHook() { String code = template2.withArgs().render(); String expected = """ + { Hello - World"""; + World + }"""; + checkEQ(code, expected); + } + + public static void testHook2() { + var hook1 = new Hook("Hook1"); + + var template1 = Template.make("a", (String a) -> body("x #a x\n")); + + // Test nested use of hooks in the same template. + var template2 = Template.make(() -> body( + "{\n", + hook1.set(), // empty + "zero\n", + hook1.set( + template1.withArgs("one"), + template1.withArgs("two"), + intoHook(hook1, template1.withArgs("intoHook1a")), + intoHook(hook1, template1.withArgs("intoHook1b")), + template1.withArgs("three"), + hook1.set( + template1.withArgs("four"), + intoHook(hook1, template1.withArgs("intoHook1c")), + template1.withArgs("five") + ), + template1.withArgs("six"), + hook1.set(), // empty + template1.withArgs("seven"), + intoHook(hook1, template1.withArgs("intoHook1d")), + template1.withArgs("eight"), + hook1.set( + template1.withArgs("nine"), + intoHook(hook1, template1.withArgs("intoHook1e")), + template1.withArgs("ten") + ), + template1.withArgs("eleven") + ), + "}" + )); + + String code = template2.withArgs().render(); + String expected = + """ + { + zero + x intoHook1a x + x intoHook1b x + x intoHook1d x + x one x + x two x + x three x + x intoHook1c x + x four x + x five x + x six x + x seven x + x eight x + x intoHook1e x + x nine x + x ten x + x eleven x + }"""; checkEQ(code, expected); } From 16e81029b1cc073cb089f5d5f088ffde210f470a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 5 Feb 2025 20:58:54 +0100 Subject: [PATCH 015/212] testHookWithNestedTemplates --- .../compiler/lib/template_framework/Hook.java | 2 + .../tests/TestTemplate.java | 102 ++++++++++++++++-- 2 files changed, 98 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java index a7beb43ac72f0..30e677c160997 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java @@ -30,4 +30,6 @@ public record Hook(String name) { public HookUse set(Object... tokens) { return new HookUse(this, Arrays.asList(tokens)); } + + // TODO hook.send(...) vs intoHook(hook, ...) } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 9bc7859afa3e4..5ecba28426951 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -51,12 +51,13 @@ public class TestTemplate { public static void main(String[] args) { testSingleLine(); testMultiLine(); - testBodyElements(); + testBodyTokens(); testWithOneArguments(); testWithTwoArguments(); testRecursive(); - testHook1(); - testHook2(); + testHookSimple(); + testHookNested(); + testHookWithNestedTemplates(); //testClassInstantiator(); //testRepeat(); @@ -93,7 +94,7 @@ public static void testMultiLine() { checkEQ(code, expected); } - public static void testBodyElements() { + public static void testBodyTokens() { // We can fill the body with Objects of different types, and they get concatenated. // Lists get flattened into the body. var template = Template.make(() -> body( @@ -203,7 +204,7 @@ public static void testRecursive() { checkEQ(code, expected); } - public static void testHook1() { + public static void testHookSimple() { var hook1 = new Hook("Hook1"); var template1 = Template.make(() -> body("Hello\n")); @@ -227,7 +228,7 @@ public static void testHook1() { checkEQ(code, expected); } - public static void testHook2() { + public static void testHookNested() { var hook1 = new Hook("Hook1"); var template1 = Template.make("a", (String a) -> body("x #a x\n")); @@ -288,6 +289,95 @@ public static void testHook2() { checkEQ(code, expected); } + public static void testHookWithNestedTemplates() { + var hook1 = new Hook("Hook1"); + var hook2 = new Hook("Hook2"); + + var template1 = Template.make("a", (String a) -> body("x #a x\n")); + + var template2 = Template.make("b", (String b) -> body( + "{\n", + template1.withArgs(b + "A"), + intoHook(hook1, template1.withArgs(b + "B")), + intoHook(hook2, template1.withArgs(b + "C")), + template1.withArgs(b + "D"), + hook1.set( + template1.withArgs(b + "E"), + intoHook(hook1, template1.withArgs(b + "F")), + intoHook(hook2, template1.withArgs(b + "G")), + template1.withArgs(b + "H"), + hook2.set( + template1.withArgs(b + "I"), + intoHook(hook1, template1.withArgs(b + "J")), + intoHook(hook2, template1.withArgs(b + "K")), + template1.withArgs(b + "L") + ), + template1.withArgs(b + "M"), + intoHook(hook1, template1.withArgs(b + "N")), + intoHook(hook2, template1.withArgs(b + "O")), + template1.withArgs(b + "O") + ), + template1.withArgs(b + "P"), + intoHook(hook1, template1.withArgs(b + "Q")), + intoHook(hook2, template1.withArgs(b + "R")), + template1.withArgs(b + "S"), + "}\n" + )); + + // Test use of hooks across templates. + var template3 = Template.make(() -> body( + "{\n", + "base-A\n", + hook1.set( + "base-B\n", + hook2.set( + "base-C\n", + template2.withArgs("sub-"), + "base-D\n" + ), + "base-E\n" + ), + "base-F\n", + "}\n" + )); + + String code = template3.withArgs().render(); + String expected = + """ + { + base-A + x sub-B x + x sub-Q x + base-B + x sub-C x + x sub-G x + x sub-O x + x sub-R x + base-C + { + x sub-A x + x sub-D x + x sub-F x + x sub-J x + x sub-N x + x sub-E x + x sub-H x + x sub-K x + x sub-I x + x sub-L x + x sub-M x + x sub-O x + x sub-P x + x sub-S x + } + base-D + base-E + base-F + } + """; + checkEQ(code, expected); + } + public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { System.out.println("\"" + code + "\""); From 704659c18520d335474de66c24d51d1479789b52 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 5 Feb 2025 22:08:32 +0100 Subject: [PATCH 016/212] wip names --- .../lib/template_framework/Frame.java | 2 +- .../lib/template_framework/Renderer.java | 11 ++++-- .../lib/template_framework/Template.java | 4 +++ .../tests/TestTemplate.java | 35 +++++++++++++++++++ 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java index 860efbf3ff386..0db4c3676ea9f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java @@ -76,7 +76,7 @@ public String getContext(String key) { } String variableName(String name) { - return variableNames.computeIfAbsent(name, s -> name + Renderer.variableId++); + return variableNames.computeIfAbsent(name, s -> name + "_" + Renderer.variableId++); } // TODO ensure only use once! diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index f9669805ec11e..9e3795c14dedd 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -70,7 +70,7 @@ public static String render(TemplateUse templateUse) { return code; } - public static String $(String name) { + static String variableName(String name) { return getCurrentFrame().variableName(name); } @@ -169,8 +169,13 @@ case HookInsert(Hook hook, TemplateUse t) -> { } private static String templateString(String s, Frame frame) { - var temp = DOLLAR_NAME_PATTERN.matcher(s).replaceAll((MatchResult result) -> $(result.group(1))); - return HASHTAG_REPLACEMENT_PATTERN.matcher(temp).replaceAll((MatchResult result) -> frame.getContext(result.group(1))); + var temp = DOLLAR_NAME_PATTERN.matcher(s).replaceAll( + (MatchResult result) -> variableName(result.group(1)) + ); + return HASHTAG_REPLACEMENT_PATTERN.matcher(temp).replaceAll( + // We must escape "$", because it has a special meaning in replaceAll. + (MatchResult result) -> frame.getContext(result.group(1)).replace("$", "\\$") + ); } private static Frame frameForHook(Hook hook) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 378615f4180a2..9f68692495705 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -78,4 +78,8 @@ static InstantiatedTemplate body(Object... tokens) { static HookInsert intoHook(Hook hook, TemplateUse t) { return new HookInsert(hook, t); } + + static String $(String name) { + return Renderer.variableName(name); + } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 5ecba28426951..5776957e70b1b 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -44,6 +44,7 @@ import compiler.lib.template_framework.Template; import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.intoHook; +import static compiler.lib.template_framework.Template.$; public class TestTemplate { private static final Random RANDOM = Utils.getRandomInstance(); @@ -58,6 +59,9 @@ public static void main(String[] args) { testHookSimple(); testHookNested(); testHookWithNestedTemplates(); + testNames(); + + // TODO let //testClassInstantiator(); //testRepeat(); @@ -378,6 +382,37 @@ public static void testHookWithNestedTemplates() { checkEQ(code, expected); } + public static void testNames() { + var hook1 = new Hook("Hook1"); + + var template1 = Template.make("a", (String a) -> body("x $name #a x\n")); + + var template2 = Template.make(() -> body( + "{\n", + "$name\n", + "$name", "\n", + "y $name\n y", + "y$name y\n", + template1.withArgs("name"), // does not capture -> literal "$name" + template1.withArgs("$name"), // does not capture -> literal "$name" + template1.withArgs($("name")), // capture replacement name "name_TODO" + hook1.set( + "$name\n" + ), + "}\n" + )); + + String code = template2.withArgs().render(); + String expected = + """ + { + } + """; + checkEQ(code, expected); + } + + + public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { System.out.println("\"" + code + "\""); From ebc4297a5ba5962cac5b461e86aec7d2a18ebc29 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 6 Feb 2025 08:14:04 +0100 Subject: [PATCH 017/212] list refactor --- .../InstantiatedTemplate.java | 4 ++- .../lib/template_framework/Renderer.java | 32 +++++++++---------- .../lib/template_framework/Template.java | 3 +- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java b/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java index 59d1d85771541..932775b1c2cb2 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java @@ -23,4 +23,6 @@ package compiler.lib.template_framework; -public record InstantiatedTemplate(Object[] tokens) {} +import java.util.List; + +public record InstantiatedTemplate(List tokens) {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 9e3795c14dedd..3a482efddbd40 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -99,16 +99,9 @@ private static Frame getCurrentFrame() { private static void renderTemplateUse(TemplateUse templateUse) { Frame frame = getCurrentFrame(); - templateUse.visitArguments((name, value) -> frame.addContext(name, value.toString())); InstantiatedTemplate it = templateUse.instantiate(); - for (Object token : it.tokens()) { - renderToken(token); - } - - if (frame != getCurrentFrame()) { - throw new RendererException("Frame mismatch."); - } + renderTokenList(it.tokens()); } private static void renderToken(Object token) { @@ -120,10 +113,8 @@ private static void renderToken(Object token) { case Long s -> frame.addString(s.toString()); case Double s -> frame.addString(s.toString()); case Float s -> frame.addString(s.toString()); - case List l -> { - for (Object t : l) { - renderToken(t); - } + case List tokens -> { + renderTokenList(tokens); } case Hook h -> { throw new RendererException("Do not use Hook directly, use Hook.set: " + h); @@ -141,10 +132,9 @@ case HookUse(Hook hook, List tokens) -> { // hookFrame. Frame innerFrame = new Frame(hookFrame); currentFrame = innerFrame; - // TODO render list, and verify frames are consistent. - for (Object t : tokens) { - renderToken(t); - } + + renderTokenList(tokens); + // Close the hookFrame and innerFrame. hookFrame code comes before the // innerFrame code from the tokens. currentFrame = outerFrame; @@ -168,6 +158,16 @@ case HookInsert(Hook hook, TemplateUse t) -> { } } + private static void renderTokenList(List tokens) { + Frame frame = getCurrentFrame(); + for (Object t : tokens) { + renderToken(t); + } + if (frame != getCurrentFrame()) { + throw new RendererException("Frame mismatch."); + } + } + private static String templateString(String s, Frame frame) { var temp = DOLLAR_NAME_PATTERN.matcher(s).replaceAll( (MatchResult result) -> variableName(result.group(1)) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 9f68692495705..b681be9d19e2a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -23,6 +23,7 @@ package compiler.lib.template_framework; +import java.util.Arrays; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; @@ -72,7 +73,7 @@ public TemplateUse.TwoArgsUse withArgs(A a, B b) { } static InstantiatedTemplate body(Object... tokens) { - return new InstantiatedTemplate(tokens); + return new InstantiatedTemplate(Arrays.asList(tokens)); } static HookInsert intoHook(Hook hook, TemplateUse t) { From f5698b9c27d0f4034d153f00fa5f81d28092b496 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 6 Feb 2025 09:01:33 +0100 Subject: [PATCH 018/212] RenderInfo --- .../lib/template_framework/Frame.java | 5 -- .../lib/template_framework/Renderer.java | 57 +++++++++++++++++-- .../lib/template_framework/Template.java | 2 +- .../tests/TestTemplate.java | 10 +++- 4 files changed, 61 insertions(+), 13 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java index 0db4c3676ea9f..4714ee913241f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java @@ -33,7 +33,6 @@ class Frame { private final List codeList = new ArrayList(); private final Map hookCodeLists = new HashMap<>(); - private final Map variableNames = new HashMap<>(); private final Map context = new HashMap<>(); Frame(Frame parent) { @@ -75,10 +74,6 @@ public String getContext(String key) { throw new RendererException("Tried to interpolate field " + key + " which does not exist."); } - String variableName(String name) { - return variableNames.computeIfAbsent(name, s -> name + "_" + Renderer.variableId++); - } - // TODO ensure only use once! Code getCode() { return new Code.CodeList(codeList); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 3a482efddbd40..1c1ddb17ac032 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -39,18 +39,40 @@ private Nothing() {} private static final Pattern DOLLAR_NAME_PATTERN = Pattern.compile("\\$([a-zA-Z_][a-zA-Z0-9_]*)"); private static final Pattern HASHTAG_REPLACEMENT_PATTERN = Pattern.compile("#([a-zA-Z_][a-zA-Z0-9_]*)"); + + // TODO describe private static Frame baseFrame = null; private static Frame currentFrame = null; - static int variableId = 0; + // TODO better name. Scope and Frame? dollar-scope vs variable-scope? TemplateFrame vs CodeFrame? + private static class RenderInfo { + public static int nextDollarId = 0; + + public final RenderInfo parent; + public final int dollarId = nextDollarId++; + RenderInfo(RenderInfo parent) { + this.parent = parent; + } + + public String $(String name) { + return name + "_" + dollarId; + } + } + static RenderInfo baseInfo = null; + static RenderInfo currentInfo = null; public static String render(TemplateUse templateUse) { // Check nobody else is using the Renderer. if (baseFrame != null) { throw new RendererException("Nested render not allowed."); } + + // Setup the Renderer. baseFrame = new Frame(null); currentFrame = baseFrame; + RenderInfo.nextDollarId = 0; + baseInfo = new RenderInfo(null); + currentInfo = baseInfo; renderTemplateUse(templateUse); @@ -58,6 +80,10 @@ public static String render(TemplateUse templateUse) { if (baseFrame != currentFrame) { throw new RendererException("Renderer did not end up at base frame."); } + // Ensure RenderInfo consistency. + if (baseInfo != currentInfo) { + throw new RendererException("Renderer did not end up at base info."); + } // Collect Code to String. StringBuilder builder = new StringBuilder(); @@ -66,12 +92,15 @@ public static String render(TemplateUse templateUse) { // Release the Renderer. baseFrame = null; + currentFrame = null; + baseInfo = null; + currentInfo = null; return code; } - static String variableName(String name) { - return getCurrentFrame().variableName(name); + static String $(String name) { + return getCurrentInfo().$(name); } public static Nothing let(String key, Object value) { @@ -84,24 +113,40 @@ public static R let(String key, T value, Function block) { return block.apply(value); } - // TODO fuel + // TODO fuel - based on frame or info? public static int depth() { return getCurrentFrame().depth(); } private static Frame getCurrentFrame() { if (currentFrame == null) { - // TODO update text + // TODO update text - which methods are involved? throw new RendererException("A method such as $ or let was called outside a template rendering. Make sure you are not calling templates yourself, but use use()."); } return currentFrame; } + private static RenderInfo getCurrentInfo() { + if (currentInfo == null) { + // TODO update text - which methods are involved? + throw new RendererException("A method such as $ or let was called outside a template rendering. Make sure you are not calling templates yourself, but use use()."); + } + return currentInfo; + } + private static void renderTemplateUse(TemplateUse templateUse) { + RenderInfo info = new RenderInfo(getCurrentInfo()); + currentInfo = info; + Frame frame = getCurrentFrame(); templateUse.visitArguments((name, value) -> frame.addContext(name, value.toString())); InstantiatedTemplate it = templateUse.instantiate(); renderTokenList(it.tokens()); + + if (currentInfo != info) { + throw new RendererException("Info mismatch!"); + } + currentInfo = info.parent; } private static void renderToken(Object token) { @@ -170,7 +215,7 @@ private static void renderTokenList(List tokens) { private static String templateString(String s, Frame frame) { var temp = DOLLAR_NAME_PATTERN.matcher(s).replaceAll( - (MatchResult result) -> variableName(result.group(1)) + (MatchResult result) -> $(result.group(1)) ); return HASHTAG_REPLACEMENT_PATTERN.matcher(temp).replaceAll( // We must escape "$", because it has a special meaning in replaceAll. diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index b681be9d19e2a..3c154f295d825 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -81,6 +81,6 @@ static HookInsert intoHook(Hook hook, TemplateUse t) { } static String $(String name) { - return Renderer.variableName(name); + return Renderer.$(name); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 5776957e70b1b..de65b66589c33 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -395,7 +395,7 @@ public static void testNames() { "y$name y\n", template1.withArgs("name"), // does not capture -> literal "$name" template1.withArgs("$name"), // does not capture -> literal "$name" - template1.withArgs($("name")), // capture replacement name "name_TODO" + template1.withArgs($("name")), // capture replacement name "name_1" hook1.set( "$name\n" ), @@ -406,6 +406,14 @@ public static void testNames() { String expected = """ { + name_1 + name_1 + y name_1 + yyname_1 y + x name_2 name x + x name_3 $name x + x name_4 name_1 x + name_1 } """; checkEQ(code, expected); From 6bfe91819d0e15b23ec100fd2cc376167caf5568 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 6 Feb 2025 09:20:15 +0100 Subject: [PATCH 019/212] refactor with TemplateFrame --- .../lib/template_framework/Renderer.java | 54 ++++++++----------- .../lib/template_framework/TemplateFrame.java | 30 +++++++++++ 2 files changed, 51 insertions(+), 33 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 1c1ddb17ac032..67633c6d2d5f6 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -44,22 +44,10 @@ private Nothing() {} private static Frame baseFrame = null; private static Frame currentFrame = null; - // TODO better name. Scope and Frame? dollar-scope vs variable-scope? TemplateFrame vs CodeFrame? - private static class RenderInfo { - public static int nextDollarId = 0; - - public final RenderInfo parent; - public final int dollarId = nextDollarId++; - RenderInfo(RenderInfo parent) { - this.parent = parent; - } + private static int nextTemplateId = 0; // TODO refactor - public String $(String name) { - return name + "_" + dollarId; - } - } - static RenderInfo baseInfo = null; - static RenderInfo currentInfo = null; + static TemplateFrame baseTemplateFrame = null; + static TemplateFrame currentTemplateFrame = null; public static String render(TemplateUse templateUse) { // Check nobody else is using the Renderer. @@ -70,9 +58,9 @@ public static String render(TemplateUse templateUse) { // Setup the Renderer. baseFrame = new Frame(null); currentFrame = baseFrame; - RenderInfo.nextDollarId = 0; - baseInfo = new RenderInfo(null); - currentInfo = baseInfo; + nextTemplateId = 0; + baseTemplateFrame = new TemplateFrame(null, nextTemplateId++); + currentTemplateFrame = baseTemplateFrame; renderTemplateUse(templateUse); @@ -80,9 +68,9 @@ public static String render(TemplateUse templateUse) { if (baseFrame != currentFrame) { throw new RendererException("Renderer did not end up at base frame."); } - // Ensure RenderInfo consistency. - if (baseInfo != currentInfo) { - throw new RendererException("Renderer did not end up at base info."); + // Ensure TemplateFrame consistency. + if (baseTemplateFrame != currentTemplateFrame) { + throw new RendererException("Renderer did not end up at base templateFrame."); } // Collect Code to String. @@ -93,14 +81,14 @@ public static String render(TemplateUse templateUse) { // Release the Renderer. baseFrame = null; currentFrame = null; - baseInfo = null; - currentInfo = null; + baseTemplateFrame = null; + currentTemplateFrame = null; return code; } static String $(String name) { - return getCurrentInfo().$(name); + return getCurrentTemplateFrame().$(name); } public static Nothing let(String key, Object value) { @@ -113,7 +101,7 @@ public static R let(String key, T value, Function block) { return block.apply(value); } - // TODO fuel - based on frame or info? + // TODO fuel - based on frame or templateFrame? public static int depth() { return getCurrentFrame().depth(); } @@ -126,27 +114,27 @@ private static Frame getCurrentFrame() { return currentFrame; } - private static RenderInfo getCurrentInfo() { - if (currentInfo == null) { + private static TemplateFrame getCurrentTemplateFrame() { + if (currentTemplateFrame == null) { // TODO update text - which methods are involved? throw new RendererException("A method such as $ or let was called outside a template rendering. Make sure you are not calling templates yourself, but use use()."); } - return currentInfo; + return currentTemplateFrame; } private static void renderTemplateUse(TemplateUse templateUse) { - RenderInfo info = new RenderInfo(getCurrentInfo()); - currentInfo = info; + TemplateFrame templateFrame = new TemplateFrame(getCurrentTemplateFrame(), nextTemplateId++); + currentTemplateFrame = templateFrame; Frame frame = getCurrentFrame(); templateUse.visitArguments((name, value) -> frame.addContext(name, value.toString())); InstantiatedTemplate it = templateUse.instantiate(); renderTokenList(it.tokens()); - if (currentInfo != info) { - throw new RendererException("Info mismatch!"); + if (currentTemplateFrame != templateFrame) { + throw new RendererException("TemplateFrame mismatch!"); } - currentInfo = info.parent; + currentTemplateFrame = currentTemplateFrame.parent(); } private static void renderToken(Object token) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java new file mode 100644 index 0000000000000..8f62ae5e41d65 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java @@ -0,0 +1,30 @@ +/* + * 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; + +record TemplateFrame(TemplateFrame parent, int id) { + public String $(String name) { + return name + "_" + id; + } +} From fef2c1efc786d5b0195deff71398830a1149b00d Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 6 Feb 2025 09:32:16 +0100 Subject: [PATCH 020/212] dollar with hooks --- .../tests/TestTemplate.java | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index de65b66589c33..41c2059d410e7 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -387,40 +387,66 @@ public static void testNames() { var template1 = Template.make("a", (String a) -> body("x $name #a x\n")); - var template2 = Template.make(() -> body( + var template2 = Template.make("a", (String a) -> body( + "{\n", + "y $name #a y\n", + template1.withArgs($("name")), + "}\n" + )); + + var template3 = Template.make(() -> body( "{\n", "$name\n", "$name", "\n", - "y $name\n y", - "y$name y\n", + "z $name z\n", + "z$name z\n", template1.withArgs("name"), // does not capture -> literal "$name" template1.withArgs("$name"), // does not capture -> literal "$name" template1.withArgs($("name")), // capture replacement name "name_1" hook1.set( "$name\n" ), + "break\n", + hook1.set( + "one\n", + intoHook(hook1, template1.withArgs($("name"))), + "two\n", + template1.withArgs($("name")), + "three\n", + intoHook(hook1, template2.withArgs($("name"))), + "four\n" + ), "}\n" )); - String code = template2.withArgs().render(); + String code = template3.withArgs().render(); String expected = """ { name_1 name_1 - y name_1 - yyname_1 y + z name_1 z + zname_1 z x name_2 name x x name_3 $name x x name_4 name_1 x name_1 + break + x name_5 name_1 x + { + y name_7 name_1 y + x name_8 name_7 x + } + one + two + x name_6 name_1 x + three + four } """; checkEQ(code, expected); } - - public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { System.out.println("\"" + code + "\""); From 7be1d97c4043c265202b4f463b2062e1db242d55 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 6 Feb 2025 16:15:01 +0100 Subject: [PATCH 021/212] fix order of nested hook insertions --- .../lib/template_framework/Renderer.java | 20 ++++++- .../tests/TestTemplate.java | 57 +++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 67633c6d2d5f6..156d1fee347a5 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -175,15 +175,31 @@ case HookUse(Hook hook, List tokens) -> { currentFrame.addCode(innerFrame.getCode()); } case HookInsert(Hook hook, TemplateUse t) -> { + // Switch to hook frame. Frame hookFrame = frameForHook(hook); - currentFrame = hookFrame; // switch to hook frame. + + // Use a transparent nested Frame. We need a frame so that the code generated + // by the TemplateUse can be collected, and hook insertions from it can still + // be made to the hookFrame before the code from the TemplateUse is added to + // the hookFrame. + // But the frame must be transparent, so that its name definitions go out to + // the hookFrame, and are not limited to the Frame for the TemplateUse. + currentFrame = new Frame(hookFrame); + // TODO make transparent for names + renderTemplateUse(t); - currentFrame = frame; // switch back. + + hookFrame.addCode(currentFrame.getCode()); + + // Switch back from hook frame to caller frame. + currentFrame = frame; } case TemplateUse t -> { // Use a nested Frame. currentFrame = new Frame(frame); + renderTemplateUse(t); + frame.addCode(currentFrame.getCode()); currentFrame = frame; } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 41c2059d410e7..8f24aae517ab6 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -59,6 +59,7 @@ public static void main(String[] args) { testHookSimple(); testHookNested(); testHookWithNestedTemplates(); + testHookRecursion(); testNames(); // TODO let @@ -382,6 +383,62 @@ public static void testHookWithNestedTemplates() { checkEQ(code, expected); } + public static void testHookRecursion() { + var hook1 = new Hook("Hook1"); + + var template1 = Template.make("a", (String a) -> body("x #a x\n")); + + var template2 = Template.make("b", (String b) -> body( + "<\n", + template1.withArgs(b + "A"), + intoHook(hook1, template1.withArgs(b + "B")), // sub-B is rendered before template2. + template1.withArgs(b + "C"), + "inner-hook-start\n", + hook1.set( + "inner-hook-end\n", + template1.withArgs(b + "E"), + intoHook(hook1, template1.withArgs(b + "E")), + template1.withArgs(b + "F") + ), + ">\n" + )); + + // Test use of hooks across templates. + var template3 = Template.make(() -> body( + "{\n", + "hook-start\n", + hook1.set( + "hook-end\n", + intoHook(hook1, template2.withArgs("sub-")), + "base-C\n" + ), + "base-D\n", + "}\n" + )); + + String code = template3.withArgs().render(); + String expected = + """ + { + hook-start + x sub-B x + < + x sub-A x + x sub-C x + inner-hook-start + x sub-E x + inner-hook-end + x sub-E x + x sub-F x + > + hook-end + base-C + base-D + } + """; + checkEQ(code, expected); + } + public static void testNames() { var hook1 = new Hook("Hook1"); From 7abba9b8660d37e21faf3710426011620851245f Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 6 Feb 2025 17:33:07 +0100 Subject: [PATCH 022/212] LetUse, hashtag replacements refactor, TemplateFrame refactor --- .../lib/template_framework/Frame.java | 13 ----- .../lib/template_framework/LetUse.java | 26 +++++++++ .../lib/template_framework/Renderer.java | 41 ++++++------- .../lib/template_framework/Template.java | 4 ++ .../lib/template_framework/TemplateFrame.java | 57 ++++++++++++++++++- .../tests/TestTemplate.java | 42 +++++++++++++- 6 files changed, 142 insertions(+), 41 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/LetUse.java diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java index 4714ee913241f..0283ac2e4f945 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java @@ -33,8 +33,6 @@ class Frame { private final List codeList = new ArrayList(); private final Map hookCodeLists = new HashMap<>(); - private final Map context = new HashMap<>(); - Frame(Frame parent) { this.parent = parent; } @@ -63,17 +61,6 @@ boolean hasHook(Hook hook) { return hookCodeLists.containsKey(hook); } - public void addContext(String key, String value) { - context.put(key, value); - } - - public String getContext(String key) { - if (context.containsKey(key)) { - return context.get(key); - } - throw new RendererException("Tried to interpolate field " + key + " which does not exist."); - } - // TODO ensure only use once! Code getCode() { return new Code.CodeList(codeList); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/LetUse.java b/test/hotspot/jtreg/compiler/lib/template_framework/LetUse.java new file mode 100644 index 0000000000000..c225be4765049 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/LetUse.java @@ -0,0 +1,26 @@ +/* + * 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; + +public record LetUse(String key, String value) {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 156d1fee347a5..0c87c8839c836 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -31,12 +31,6 @@ import java.util.stream.Stream; public abstract class Renderer { - public static final class Nothing { - public static final Nothing instance = new Nothing(); - - private Nothing() {} - } - private static final Pattern DOLLAR_NAME_PATTERN = Pattern.compile("\\$([a-zA-Z_][a-zA-Z0-9_]*)"); private static final Pattern HASHTAG_REPLACEMENT_PATTERN = Pattern.compile("#([a-zA-Z_][a-zA-Z0-9_]*)"); @@ -59,7 +53,7 @@ public static String render(TemplateUse templateUse) { baseFrame = new Frame(null); currentFrame = baseFrame; nextTemplateId = 0; - baseTemplateFrame = new TemplateFrame(null, nextTemplateId++); + baseTemplateFrame = TemplateFrame.makeBorder(null, nextTemplateId++); currentTemplateFrame = baseTemplateFrame; renderTemplateUse(templateUse); @@ -91,15 +85,11 @@ public static String render(TemplateUse templateUse) { return getCurrentTemplateFrame().$(name); } - public static Nothing let(String key, Object value) { - getCurrentFrame().addContext(key, value.toString()); - return Nothing.instance; - } - - public static R let(String key, T value, Function block) { - getCurrentFrame().addContext(key, value.toString()); - return block.apply(value); - } + // TODO + //public static R let(String key, T value, Function block) { + // getCurrentFrame().addContext(key, value.toString()); + // return block.apply(value); + //} // TODO fuel - based on frame or templateFrame? public static int depth() { @@ -123,25 +113,23 @@ private static TemplateFrame getCurrentTemplateFrame() { } private static void renderTemplateUse(TemplateUse templateUse) { - TemplateFrame templateFrame = new TemplateFrame(getCurrentTemplateFrame(), nextTemplateId++); + TemplateFrame templateFrame = TemplateFrame.makeBorder(getCurrentTemplateFrame(), nextTemplateId++); currentTemplateFrame = templateFrame; - Frame frame = getCurrentFrame(); - templateUse.visitArguments((name, value) -> frame.addContext(name, value.toString())); + templateUse.visitArguments((name, value) -> templateFrame.addHashtagReplacement(name, value.toString())); InstantiatedTemplate it = templateUse.instantiate(); renderTokenList(it.tokens()); if (currentTemplateFrame != templateFrame) { throw new RendererException("TemplateFrame mismatch!"); } - currentTemplateFrame = currentTemplateFrame.parent(); + currentTemplateFrame = currentTemplateFrame.parent; } private static void renderToken(Object token) { Frame frame = getCurrentFrame(); switch (token) { - case Nothing x -> {} - case String s -> frame.addString(templateString(s, frame)); + case String s -> frame.addString(templateString(s)); case Integer s -> frame.addString(s.toString()); case Long s -> frame.addString(s.toString()); case Double s -> frame.addString(s.toString()); @@ -149,6 +137,9 @@ private static void renderToken(Object token) { case List tokens -> { renderTokenList(tokens); } + case LetUse(String key, String value) -> { + getCurrentTemplateFrame().addHashtagReplacement(key, value.toString()); + } case Hook h -> { throw new RendererException("Do not use Hook directly, use Hook.set: " + h); } @@ -217,13 +208,13 @@ private static void renderTokenList(List tokens) { } } - private static String templateString(String s, Frame frame) { + private static String templateString(String s) { var temp = DOLLAR_NAME_PATTERN.matcher(s).replaceAll( - (MatchResult result) -> $(result.group(1)) + (MatchResult result) -> getCurrentTemplateFrame().$(result.group(1)) ); return HASHTAG_REPLACEMENT_PATTERN.matcher(temp).replaceAll( // We must escape "$", because it has a special meaning in replaceAll. - (MatchResult result) -> frame.getContext(result.group(1)).replace("$", "\\$") + (MatchResult result) -> getCurrentTemplateFrame().getHashtagReplacement(result.group(1)).replace("$", "\\$") ); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 3c154f295d825..094517f4e0c80 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -83,4 +83,8 @@ static HookInsert intoHook(Hook hook, TemplateUse t) { static String $(String name) { return Renderer.$(name); } + + static LetUse let(String key, Object value) { + return new LetUse(key, value.toString()); + } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java index 8f62ae5e41d65..e75dfc072e998 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java @@ -23,8 +23,63 @@ package compiler.lib.template_framework; -record TemplateFrame(TemplateFrame parent, int id) { +import java.util.HashMap; +import java.util.Map; + +// We need frames for +// - Variables: +// - normal template borders are opaque, but not for intoHook +// - hook scopes are also opaque +// +// - hashtag replacement: +// - template border opaque +// - hook scope transparent +// +// - Parent: +// - via calls +// - code / variable scopes + +class TemplateFrame { + public final TemplateFrame parent; + final int id; + + enum Kind { INTERNAL, BORDER } + private final Kind kind; + + final Map hashtagReplacements = new HashMap<>(); + + private TemplateFrame(TemplateFrame parent, int id, Kind kind) { + this.parent = parent; + this.id = id; + this.kind = kind; + } + + public static TemplateFrame makeBorder(TemplateFrame parent, int id) { + return new TemplateFrame(parent, id, Kind.BORDER); + } + + public static TemplateFrame makeInternal(TemplateFrame parent) { + TemplateFrame frame = new TemplateFrame(parent, parent.id, Kind.INTERNAL); + frame.hashtagReplacements.putAll(parent.hashtagReplacements); + return frame; + } + public String $(String name) { return name + "_" + id; } + + void addHashtagReplacement(String key, String value) { + if (!hashtagReplacements.containsKey(key)) { + hashtagReplacements.put(key, value); + return; + } + throw new RendererException("Duplicate hashtag replacement for #" + key); + } + + String getHashtagReplacement(String key) { + if (hashtagReplacements.containsKey(key)) { + return hashtagReplacements.get(key); + } + throw new RendererException("Missing hashtag replacement for #" + key); + } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 8f24aae517ab6..198359f3f2856 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -45,6 +45,7 @@ import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.intoHook; import static compiler.lib.template_framework.Template.$; +import static compiler.lib.template_framework.Template.let; public class TestTemplate { private static final Random RANDOM = Utils.getRandomInstance(); @@ -61,8 +62,7 @@ public static void main(String[] args) { testHookWithNestedTemplates(); testHookRecursion(); testNames(); - - // TODO let + testLet(); //testClassInstantiator(); //testRepeat(); @@ -504,6 +504,44 @@ public static void testNames() { checkEQ(code, expected); } + public static void testLet() { + var hook1 = new Hook("Hook1"); + + var template1 = Template.make("a", (String a) -> body( + "{\n", + "y #a y\n", + let("b", "<" + a + ">"), + "y #b y\n", + "}\n" + )); + + var template2 = Template.make(() -> body( + "{\n", + let("x", "abc"), + template1.withArgs("alpha"), + "break\n", + "x1 = #x\n", + hook1.set( + "x2 = #x\n", + template1.withArgs("beta"), + let("y", "one"), + "y1 = #y\n" + ), + "break\n", + let("y", "two"), + "y2 = #y\n", + "}\n" + )); + + String code = template2.withArgs().render(); + String expected = + """ + + """; + checkEQ(code, expected); + } + + public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { System.out.println("\"" + code + "\""); From 179f7686ebbe52d09d4183b58d3fda3d7ca14e33 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Feb 2025 08:29:59 +0100 Subject: [PATCH 023/212] let works inside whole template --- .../tests/TestTemplate.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 198359f3f2856..79869716723c1 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -522,21 +522,35 @@ public static void testLet() { "break\n", "x1 = #x\n", hook1.set( - "x2 = #x\n", + "x2 = #x\n", // leaks inside template1.withArgs("beta"), let("y", "one"), "y1 = #y\n" ), "break\n", - let("y", "two"), - "y2 = #y\n", + "y2 = #y\n", // leaks outside "}\n" )); String code = template2.withArgs().render(); String expected = """ - + { + { + y alpha y + y y + } + break + x1 = abc + x2 = abc + { + y beta y + y y + } + y1 = one + break + y2 = one + } """; checkEQ(code, expected); } From e0982dc650da7b231532f230fd8f3a77ea55ce39 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Feb 2025 08:39:49 +0100 Subject: [PATCH 024/212] simplify TemplateFrame --- .../lib/template_framework/Renderer.java | 4 ++-- .../lib/template_framework/TemplateFrame.java | 24 +++++++------------ 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 0c87c8839c836..2df583f18ad10 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -53,7 +53,7 @@ public static String render(TemplateUse templateUse) { baseFrame = new Frame(null); currentFrame = baseFrame; nextTemplateId = 0; - baseTemplateFrame = TemplateFrame.makeBorder(null, nextTemplateId++); + baseTemplateFrame = new TemplateFrame(null, nextTemplateId++); currentTemplateFrame = baseTemplateFrame; renderTemplateUse(templateUse); @@ -113,7 +113,7 @@ private static TemplateFrame getCurrentTemplateFrame() { } private static void renderTemplateUse(TemplateUse templateUse) { - TemplateFrame templateFrame = TemplateFrame.makeBorder(getCurrentTemplateFrame(), nextTemplateId++); + TemplateFrame templateFrame = new TemplateFrame(getCurrentTemplateFrame(), nextTemplateId++); currentTemplateFrame = templateFrame; templateUse.visitArguments((name, value) -> templateFrame.addHashtagReplacement(name, value.toString())); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java index e75dfc072e998..1ee085f243473 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.Map; +// TODO remove / move elsewhere // We need frames for // - Variables: // - normal template borders are opaque, but not for intoHook @@ -39,29 +40,22 @@ // - via calls // - code / variable scopes +/** + * The {@link TemplateFrame} is the frame for a {@link TemplateUse}. It ensures + * that each template use has its own unique {@link id} used to deconflict names + * using {@link $}. It also has a set of hashtag replacements, which combine the + * key-value pairs from the template argument and the {@link let} definitions. + * The {@link parent} relationship provides a trace for the use chain of templates. + */ class TemplateFrame { public final TemplateFrame parent; final int id; - enum Kind { INTERNAL, BORDER } - private final Kind kind; - final Map hashtagReplacements = new HashMap<>(); - private TemplateFrame(TemplateFrame parent, int id, Kind kind) { + TemplateFrame(TemplateFrame parent, int id) { this.parent = parent; this.id = id; - this.kind = kind; - } - - public static TemplateFrame makeBorder(TemplateFrame parent, int id) { - return new TemplateFrame(parent, id, Kind.BORDER); - } - - public static TemplateFrame makeInternal(TemplateFrame parent) { - TemplateFrame frame = new TemplateFrame(parent, parent.id, Kind.INTERNAL); - frame.hashtagReplacements.putAll(parent.hashtagReplacements); - return frame; } public String $(String name) { From 397f16eb4e4ae9bbd8bbe83c491574db0b7f35ab Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Feb 2025 08:57:01 +0100 Subject: [PATCH 025/212] Frame -> CodeFrame refactor --- .../{Frame.java => CodeFrame.java} | 7 +- .../lib/template_framework/Renderer.java | 126 +++++++++--------- 2 files changed, 68 insertions(+), 65 deletions(-) rename test/hotspot/jtreg/compiler/lib/template_framework/{Frame.java => CodeFrame.java} (94%) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java similarity index 94% rename from test/hotspot/jtreg/compiler/lib/template_framework/Frame.java rename to test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java index 0283ac2e4f945..bd8d25a0a0b70 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Frame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java @@ -28,15 +28,16 @@ import java.util.ArrayList; import java.util.List; -class Frame { - public final Frame parent; +class CodeFrame { + public final CodeFrame parent; private final List codeList = new ArrayList(); private final Map hookCodeLists = new HashMap<>(); - Frame(Frame parent) { + CodeFrame(CodeFrame parent) { this.parent = parent; } + // TODO move depth to TemplateFrame public int depth() { if (parent == null) { return 0; } return parent.depth() + 1; diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 2df583f18ad10..89da7e515aeec 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -35,8 +35,8 @@ public abstract class Renderer { private static final Pattern HASHTAG_REPLACEMENT_PATTERN = Pattern.compile("#([a-zA-Z_][a-zA-Z0-9_]*)"); // TODO describe - private static Frame baseFrame = null; - private static Frame currentFrame = null; + private static CodeFrame baseCodeFrame = null; + private static CodeFrame currentCodeFrame = null; private static int nextTemplateId = 0; // TODO refactor @@ -45,36 +45,36 @@ public abstract class Renderer { public static String render(TemplateUse templateUse) { // Check nobody else is using the Renderer. - if (baseFrame != null) { + if (baseCodeFrame != null) { throw new RendererException("Nested render not allowed."); } // Setup the Renderer. - baseFrame = new Frame(null); - currentFrame = baseFrame; + baseCodeFrame = new CodeFrame(null); + currentCodeFrame = baseCodeFrame; nextTemplateId = 0; baseTemplateFrame = new TemplateFrame(null, nextTemplateId++); currentTemplateFrame = baseTemplateFrame; renderTemplateUse(templateUse); - // Ensure Frame consistency. - if (baseFrame != currentFrame) { - throw new RendererException("Renderer did not end up at base frame."); + // Ensure CodeFrame consistency. + if (baseCodeFrame != currentCodeFrame) { + throw new RendererException("Renderer did not end up at base CodeFrame."); } // Ensure TemplateFrame consistency. if (baseTemplateFrame != currentTemplateFrame) { - throw new RendererException("Renderer did not end up at base templateFrame."); + throw new RendererException("Renderer did not end up at base TemplateFrame."); } // Collect Code to String. StringBuilder builder = new StringBuilder(); - baseFrame.getCode().renderTo(builder); + baseCodeFrame.getCode().renderTo(builder); String code = builder.toString(); // Release the Renderer. - baseFrame = null; - currentFrame = null; + baseCodeFrame = null; + currentCodeFrame = null; baseTemplateFrame = null; currentTemplateFrame = null; @@ -91,17 +91,17 @@ public static String render(TemplateUse templateUse) { // return block.apply(value); //} - // TODO fuel - based on frame or templateFrame? + // TODO fuel - based on codeFrame or templateFrame? public static int depth() { - return getCurrentFrame().depth(); + return getCurrentCodeFrame().depth(); } - private static Frame getCurrentFrame() { - if (currentFrame == null) { + private static CodeFrame getCurrentCodeFrame() { + if (currentCodeFrame == null) { // TODO update text - which methods are involved? throw new RendererException("A method such as $ or let was called outside a template rendering. Make sure you are not calling templates yourself, but use use()."); } - return currentFrame; + return currentCodeFrame; } private static TemplateFrame getCurrentTemplateFrame() { @@ -127,13 +127,13 @@ private static void renderTemplateUse(TemplateUse templateUse) { } private static void renderToken(Object token) { - Frame frame = getCurrentFrame(); + CodeFrame codeFrame = getCurrentCodeFrame(); switch (token) { - case String s -> frame.addString(templateString(s)); - case Integer s -> frame.addString(s.toString()); - case Long s -> frame.addString(s.toString()); - case Double s -> frame.addString(s.toString()); - case Float s -> frame.addString(s.toString()); + case String s -> codeFrame.addString(templateString(s)); + case Integer s -> codeFrame.addString(s.toString()); + case Long s -> codeFrame.addString(s.toString()); + case Double s -> codeFrame.addString(s.toString()); + case Float s -> codeFrame.addString(s.toString()); case List tokens -> { renderTokenList(tokens); } @@ -144,67 +144,69 @@ case LetUse(String key, String value) -> { throw new RendererException("Do not use Hook directly, use Hook.set: " + h); } case HookUse(Hook hook, List tokens) -> { - Frame outerFrame = getCurrentFrame(); + // TODO describe and maybe rename to HookSetUse + CodeFrame outerCodeFrame = getCurrentCodeFrame(); - // We need a frame to which the hook can insert code. That way, name - // definitions at the hook cannot excape the hookFrame. - Frame hookFrame = new Frame(outerFrame); - hookFrame.addHook(hook); + // We need a CodeFrame to which the hook can insert code. That way, name + // definitions at the hook cannot excape the hookCodeFrame. + CodeFrame hookCodeFrame = new CodeFrame(outerCodeFrame); + hookCodeFrame.addHook(hook); - // We need a frame where the tokens can be rendered. That way, name - // definitions from the tokens cannot escape the innerFrame to the - // hookFrame. - Frame innerFrame = new Frame(hookFrame); - currentFrame = innerFrame; + // We need a CodeFrame where the tokens can be rendered. That way, name + // definitions from the tokens cannot escape the innerCodeFrame to the + // hookCodeFrame. + CodeFrame innerCodeFrame = new CodeFrame(hookCodeFrame); + currentCodeFrame = innerCodeFrame; renderTokenList(tokens); - // Close the hookFrame and innerFrame. hookFrame code comes before the - // innerFrame code from the tokens. - currentFrame = outerFrame; - currentFrame.addCode(hookFrame.getCode()); - currentFrame.addCode(innerFrame.getCode()); + // Close the hookCodeFrame and innerCodeFrame. hookCodeFrame code comes before the + // innerCodeFrame code from the tokens. + currentCodeFrame = outerCodeFrame; + currentCodeFrame.addCode(hookCodeFrame.getCode()); + currentCodeFrame.addCode(innerCodeFrame.getCode()); } case HookInsert(Hook hook, TemplateUse t) -> { - // Switch to hook frame. - Frame hookFrame = frameForHook(hook); + // TODO describe and maybe rename to IntoHookUse + // Switch to hook CodeFrame. + CodeFrame hookCodeFrame = codeFrameForHook(hook); - // Use a transparent nested Frame. We need a frame so that the code generated + // Use a transparent nested CodeFrame. We need a CodeFrame so that the code generated // by the TemplateUse can be collected, and hook insertions from it can still - // be made to the hookFrame before the code from the TemplateUse is added to - // the hookFrame. - // But the frame must be transparent, so that its name definitions go out to - // the hookFrame, and are not limited to the Frame for the TemplateUse. - currentFrame = new Frame(hookFrame); + // be made to the hookCodeFrame before the code from the TemplateUse is added to + // the hookCodeFrame. + // But the CodeFrame must be transparent, so that its name definitions go out to + // the hookCodeFrame, and are not limited to the CodeFrame for the TemplateUse. + currentCodeFrame = new CodeFrame(hookCodeFrame); // TODO make transparent for names renderTemplateUse(t); - hookFrame.addCode(currentFrame.getCode()); + hookCodeFrame.addCode(currentCodeFrame.getCode()); - // Switch back from hook frame to caller frame. - currentFrame = frame; + // Switch back from hook CodeFrame to caller CodeFrame. + currentCodeFrame = codeFrame; } case TemplateUse t -> { - // Use a nested Frame. - currentFrame = new Frame(frame); + // Use a nested CodeFrame. + currentCodeFrame = new CodeFrame(codeFrame); renderTemplateUse(t); - frame.addCode(currentFrame.getCode()); - currentFrame = frame; + codeFrame.addCode(currentCodeFrame.getCode()); + currentCodeFrame = codeFrame; } default -> throw new RendererException("body contained unexpected token: " + token); } } private static void renderTokenList(List tokens) { - Frame frame = getCurrentFrame(); + CodeFrame codeFrame = getCurrentCodeFrame(); for (Object t : tokens) { renderToken(t); } - if (frame != getCurrentFrame()) { - throw new RendererException("Frame mismatch."); + if (codeFrame != getCurrentCodeFrame()) { + throw new RendererException("CodeFrame mismatch."); } } @@ -218,13 +220,13 @@ private static String templateString(String s) { ); } - private static Frame frameForHook(Hook hook) { - Frame frame = getCurrentFrame(); - while (frame != null) { - if (frame.hasHook(hook)) { - return frame; + private static CodeFrame codeFrameForHook(Hook hook) { + CodeFrame codeFrame = getCurrentCodeFrame(); + while (codeFrame != null) { + if (codeFrame.hasHook(hook)) { + return codeFrame; } - frame = frame.parent; + codeFrame = codeFrame.parent; } throw new RendererException("hook " + hook.name() + " was referenced but not found!"); } From a5b4833cff7b8e9b4a6400c5a4ac1c2711dd71bc Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Feb 2025 09:35:15 +0100 Subject: [PATCH 026/212] rm public for a field --- .../jtreg/compiler/lib/template_framework/TemplateFrame.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java index 1ee085f243473..93f3c0ca198f9 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java @@ -48,9 +48,8 @@ * The {@link parent} relationship provides a trace for the use chain of templates. */ class TemplateFrame { - public final TemplateFrame parent; + final TemplateFrame parent; final int id; - final Map hashtagReplacements = new HashMap<>(); TemplateFrame(TemplateFrame parent, int id) { From 61caf00f9997fb8eb672631a3dec52c8e6c355fa Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Feb 2025 10:46:38 +0100 Subject: [PATCH 027/212] Token refactoring --- .../compiler/lib/template_framework/Hook.java | 4 +- .../lib/template_framework/HookIntoToken.java | 26 ++++++++ .../{HookUse.java => HookSetToken.java} | 2 +- .../InstantiatedTemplate.java | 2 +- .../{HookInsert.java => LetToken.java} | 2 +- .../lib/template_framework/Renderer.java | 47 ++++++-------- .../{LetUse.java => StringToken.java} | 2 +- .../lib/template_framework/Template.java | 22 +++---- .../lib/template_framework/TemplateFrame.java | 2 +- ...TemplateUse.java => TemplateWithArgs.java} | 13 ++-- .../lib/template_framework/Token.java | 63 +++++++++++++++++++ 11 files changed, 134 insertions(+), 51 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/HookIntoToken.java rename test/hotspot/jtreg/compiler/lib/template_framework/{HookUse.java => HookSetToken.java} (93%) rename test/hotspot/jtreg/compiler/lib/template_framework/{HookInsert.java => LetToken.java} (94%) rename test/hotspot/jtreg/compiler/lib/template_framework/{LetUse.java => StringToken.java} (95%) rename test/hotspot/jtreg/compiler/lib/template_framework/{TemplateUse.java => TemplateWithArgs.java} (85%) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/Token.java diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java index 30e677c160997..3621fe187b270 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java @@ -27,8 +27,8 @@ import java.util.Arrays; public record Hook(String name) { - public HookUse set(Object... tokens) { - return new HookUse(this, Arrays.asList(tokens)); + public HookSetToken set(Object... tokens) { + return new HookSetToken(this, Token.parse(Arrays.asList(tokens))); } // TODO hook.send(...) vs intoHook(hook, ...) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/HookIntoToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/HookIntoToken.java new file mode 100644 index 0000000000000..73ecbf508c638 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/HookIntoToken.java @@ -0,0 +1,26 @@ +/* + * 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; + +public record HookIntoToken(Hook hook, TemplateWithArgs templateWithArgs) implements Token {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/HookUse.java b/test/hotspot/jtreg/compiler/lib/template_framework/HookSetToken.java similarity index 93% rename from test/hotspot/jtreg/compiler/lib/template_framework/HookUse.java rename to test/hotspot/jtreg/compiler/lib/template_framework/HookSetToken.java index 9ecde8f01dc10..f20cb3214c9ab 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/HookUse.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/HookSetToken.java @@ -25,4 +25,4 @@ import java.util.List; -public record HookUse(Hook hook, List tokens) {} +public record HookSetToken(Hook hook, List tokens) implements Token {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java b/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java index 932775b1c2cb2..30d31ada287cc 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java @@ -25,4 +25,4 @@ import java.util.List; -public record InstantiatedTemplate(List tokens) {} +public record InstantiatedTemplate(List tokens) {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/HookInsert.java b/test/hotspot/jtreg/compiler/lib/template_framework/LetToken.java similarity index 94% rename from test/hotspot/jtreg/compiler/lib/template_framework/HookInsert.java rename to test/hotspot/jtreg/compiler/lib/template_framework/LetToken.java index 32e8f5a434f0a..83221680dd9eb 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/HookInsert.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/LetToken.java @@ -23,4 +23,4 @@ package compiler.lib.template_framework; -public record HookInsert(Hook hook, TemplateUse templateUse) {} +public record LetToken(String key, String value) implements Token {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 89da7e515aeec..1e4c24f266bbe 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -43,7 +43,7 @@ public abstract class Renderer { static TemplateFrame baseTemplateFrame = null; static TemplateFrame currentTemplateFrame = null; - public static String render(TemplateUse templateUse) { + public static String render(TemplateWithArgs templateWithArgs) { // Check nobody else is using the Renderer. if (baseCodeFrame != null) { throw new RendererException("Nested render not allowed."); @@ -56,7 +56,7 @@ public static String render(TemplateUse templateUse) { baseTemplateFrame = new TemplateFrame(null, nextTemplateId++); currentTemplateFrame = baseTemplateFrame; - renderTemplateUse(templateUse); + renderTemplateWithArgs(templateWithArgs); // Ensure CodeFrame consistency. if (baseCodeFrame != currentCodeFrame) { @@ -112,12 +112,12 @@ private static TemplateFrame getCurrentTemplateFrame() { return currentTemplateFrame; } - private static void renderTemplateUse(TemplateUse templateUse) { + private static void renderTemplateWithArgs(TemplateWithArgs templateWithArgs) { TemplateFrame templateFrame = new TemplateFrame(getCurrentTemplateFrame(), nextTemplateId++); currentTemplateFrame = templateFrame; - templateUse.visitArguments((name, value) -> templateFrame.addHashtagReplacement(name, value.toString())); - InstantiatedTemplate it = templateUse.instantiate(); + templateWithArgs.visitArguments((name, value) -> templateFrame.addHashtagReplacement(name, value.toString())); + InstantiatedTemplate it = templateWithArgs.instantiate(); renderTokenList(it.tokens()); if (currentTemplateFrame != templateFrame) { @@ -126,24 +126,14 @@ private static void renderTemplateUse(TemplateUse templateUse) { currentTemplateFrame = currentTemplateFrame.parent; } - private static void renderToken(Object token) { + private static void renderToken(Token token) { CodeFrame codeFrame = getCurrentCodeFrame(); switch (token) { - case String s -> codeFrame.addString(templateString(s)); - case Integer s -> codeFrame.addString(s.toString()); - case Long s -> codeFrame.addString(s.toString()); - case Double s -> codeFrame.addString(s.toString()); - case Float s -> codeFrame.addString(s.toString()); - case List tokens -> { - renderTokenList(tokens); - } - case LetUse(String key, String value) -> { + case StringToken(String s) -> codeFrame.addString(templateString(s)); + case LetToken(String key, String value) -> { getCurrentTemplateFrame().addHashtagReplacement(key, value.toString()); } - case Hook h -> { - throw new RendererException("Do not use Hook directly, use Hook.set: " + h); - } - case HookUse(Hook hook, List tokens) -> { + case HookSetToken(Hook hook, List tokens) -> { // TODO describe and maybe rename to HookSetUse CodeFrame outerCodeFrame = getCurrentCodeFrame(); @@ -166,43 +156,42 @@ case HookUse(Hook hook, List tokens) -> { currentCodeFrame.addCode(hookCodeFrame.getCode()); currentCodeFrame.addCode(innerCodeFrame.getCode()); } - case HookInsert(Hook hook, TemplateUse t) -> { + case HookIntoToken(Hook hook, TemplateWithArgs t) -> { // TODO describe and maybe rename to IntoHookUse // Switch to hook CodeFrame. CodeFrame hookCodeFrame = codeFrameForHook(hook); // Use a transparent nested CodeFrame. We need a CodeFrame so that the code generated - // by the TemplateUse can be collected, and hook insertions from it can still - // be made to the hookCodeFrame before the code from the TemplateUse is added to + // by the TemplateWithArgs can be collected, and hook insertions from it can still + // be made to the hookCodeFrame before the code from the TemplateWithArgs is added to // the hookCodeFrame. // But the CodeFrame must be transparent, so that its name definitions go out to - // the hookCodeFrame, and are not limited to the CodeFrame for the TemplateUse. + // the hookCodeFrame, and are not limited to the CodeFrame for the TemplateWithArgs. currentCodeFrame = new CodeFrame(hookCodeFrame); // TODO make transparent for names - renderTemplateUse(t); + renderTemplateWithArgs(t); hookCodeFrame.addCode(currentCodeFrame.getCode()); // Switch back from hook CodeFrame to caller CodeFrame. currentCodeFrame = codeFrame; } - case TemplateUse t -> { + case TemplateWithArgs t -> { // Use a nested CodeFrame. currentCodeFrame = new CodeFrame(codeFrame); - renderTemplateUse(t); + renderTemplateWithArgs(t); codeFrame.addCode(currentCodeFrame.getCode()); currentCodeFrame = codeFrame; } - default -> throw new RendererException("body contained unexpected token: " + token); } } - private static void renderTokenList(List tokens) { + private static void renderTokenList(List tokens) { CodeFrame codeFrame = getCurrentCodeFrame(); - for (Object t : tokens) { + for (Token t : tokens) { renderToken(t); } if (codeFrame != getCurrentCodeFrame()) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/LetUse.java b/test/hotspot/jtreg/compiler/lib/template_framework/StringToken.java similarity index 95% rename from test/hotspot/jtreg/compiler/lib/template_framework/LetUse.java rename to test/hotspot/jtreg/compiler/lib/template_framework/StringToken.java index c225be4765049..4926748e51a83 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/LetUse.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/StringToken.java @@ -23,4 +23,4 @@ package compiler.lib.template_framework; -public record LetUse(String key, String value) {} +record StringToken(String value) implements Token {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 094517f4e0c80..b43c80213de82 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -46,8 +46,8 @@ InstantiatedTemplate instantiate() { return function.get(); } - public TemplateUse.ZeroArgsUse withArgs() { - return new TemplateUse.ZeroArgsUse(this); + public TemplateWithArgs.ZeroArgsUse withArgs() { + return new TemplateWithArgs.ZeroArgsUse(this); } } @@ -56,8 +56,8 @@ InstantiatedTemplate instantiate(A a) { return function.apply(a); } - public TemplateUse.OneArgUse withArgs(A a) { - return new TemplateUse.OneArgUse<>(this, a); + public TemplateWithArgs.OneArgUse withArgs(A a) { + return new TemplateWithArgs.OneArgUse<>(this, a); } } @@ -67,24 +67,24 @@ InstantiatedTemplate instantiate(A a, B b) { return function.apply(a, b); } - public TemplateUse.TwoArgsUse withArgs(A a, B b) { - return new TemplateUse.TwoArgsUse<>(this, a, b); + public TemplateWithArgs.TwoArgsUse withArgs(A a, B b) { + return new TemplateWithArgs.TwoArgsUse<>(this, a, b); } } static InstantiatedTemplate body(Object... tokens) { - return new InstantiatedTemplate(Arrays.asList(tokens)); + return new InstantiatedTemplate(Token.parse(Arrays.asList(tokens))); } - static HookInsert intoHook(Hook hook, TemplateUse t) { - return new HookInsert(hook, t); + static HookIntoToken intoHook(Hook hook, TemplateWithArgs t) { + return new HookIntoToken(hook, t); } static String $(String name) { return Renderer.$(name); } - static LetUse let(String key, Object value) { - return new LetUse(key, value.toString()); + static LetToken let(String key, Object value) { + return new LetToken(key, value.toString()); } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java index 93f3c0ca198f9..38d80e52cfea4 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java @@ -41,7 +41,7 @@ // - code / variable scopes /** - * The {@link TemplateFrame} is the frame for a {@link TemplateUse}. It ensures + * The {@link TemplateFrame} is the frame for a {@link TemplateWithArgs}. It ensures * that each template use has its own unique {@link id} used to deconflict names * using {@link $}. It also has a set of hashtag replacements, which combine the * key-value pairs from the template argument and the {@link let} definitions. diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateUse.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java similarity index 85% rename from test/hotspot/jtreg/compiler/lib/template_framework/TemplateUse.java rename to test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java index 083eeb7614b30..6f8c15f8aef95 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateUse.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java @@ -23,8 +23,13 @@ package compiler.lib.template_framework; -public interface TemplateUse { - record ZeroArgsUse(Template.ZeroArgs zeroArgs) implements TemplateUse { +// TODO rename OneArgUse to OneArgsUse for consistency? +public sealed interface TemplateWithArgs extends Token + permits TemplateWithArgs.ZeroArgsUse, + TemplateWithArgs.OneArgUse, + TemplateWithArgs.TwoArgsUse +{ + record ZeroArgsUse(Template.ZeroArgs zeroArgs) implements TemplateWithArgs, Token { @Override public InstantiatedTemplate instantiate() { return zeroArgs.instantiate(); @@ -34,7 +39,7 @@ public InstantiatedTemplate instantiate() { public void visitArguments(ArgumentVisitor visitor) {} } - record OneArgUse(Template.OneArg oneArg, A a) implements TemplateUse { + record OneArgUse(Template.OneArg oneArg, A a) implements TemplateWithArgs, Token { @Override public InstantiatedTemplate instantiate() { return oneArg.instantiate(a); @@ -46,7 +51,7 @@ public void visitArguments(ArgumentVisitor visitor) { } } - record TwoArgsUse(Template.TwoArgs twoArgs, A a, B b) implements TemplateUse { + record TwoArgsUse(Template.TwoArgs twoArgs, A a, B b) implements TemplateWithArgs, Token { @Override public InstantiatedTemplate instantiate() { return twoArgs.instantiate(a, b); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java new file mode 100644 index 0000000000000..80605ae674fba --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.template_framework; + +import java.util.ArrayList; +import java.util.List; + +sealed interface Token permits StringToken, + TemplateWithArgs, + TemplateWithArgs.ZeroArgsUse, + TemplateWithArgs.OneArgUse, + TemplateWithArgs.TwoArgsUse, + HookSetToken, + HookIntoToken, + LetToken +{ + static List parse(Object object) { + List outputList = new ArrayList(); + parseToken(object, outputList); + return outputList; + } + + private static void parseList(List inputList, List outputList) { + for (Object o : inputList) { + parseToken(o, outputList); + } + } + + private static void parseToken(Object o, List outputList) { + switch (o) { + case Token t -> outputList.add(t); + case String s -> outputList.add(new StringToken(s)); + case Integer s -> outputList.add(new StringToken(s.toString())); + case Long s -> outputList.add(new StringToken(s.toString())); + case Double s -> outputList.add(new StringToken(s.toString())); + case Float s -> outputList.add(new StringToken(s.toString())); + case List l -> parseList(l, outputList); + //case Hook h -> { // TODO some known cases + default -> throw new IllegalArgumentException("Unexpected token: " + o); + } + } +} From f0c679a1ad426a2411c6fa2c3e96c575933d6a28 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Feb 2025 11:37:04 +0100 Subject: [PATCH 028/212] InstantiatedTemplate -> TemplateBody --- .../lib/template_framework/Renderer.java | 2 +- .../lib/template_framework/Template.java | 22 +++++++++---------- ...ntiatedTemplate.java => TemplateBody.java} | 2 +- .../template_framework/TemplateWithArgs.java | 8 +++---- 4 files changed, 17 insertions(+), 17 deletions(-) rename test/hotspot/jtreg/compiler/lib/template_framework/{InstantiatedTemplate.java => TemplateBody.java} (95%) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 1e4c24f266bbe..203998344b3cd 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -117,7 +117,7 @@ private static void renderTemplateWithArgs(TemplateWithArgs templateWithArgs) { currentTemplateFrame = templateFrame; templateWithArgs.visitArguments((name, value) -> templateFrame.addHashtagReplacement(name, value.toString())); - InstantiatedTemplate it = templateWithArgs.instantiate(); + TemplateBody it = templateWithArgs.instantiate(); renderTokenList(it.tokens()); if (currentTemplateFrame != templateFrame) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index b43c80213de82..81c5305fc5afd 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -29,20 +29,20 @@ import java.util.function.Supplier; public interface Template { - static ZeroArgs make(Supplier t) { + static ZeroArgs make(Supplier t) { return new ZeroArgs(t); } - static OneArg make(String arg0Name, Function t) { + static OneArg make(String arg0Name, Function t) { return new OneArg<>(arg0Name, t); } - static TwoArgs make(String arg0Name, String arg1Name, BiFunction t) { + static TwoArgs make(String arg0Name, String arg1Name, BiFunction t) { return new TwoArgs<>(arg0Name, arg1Name, t); } - record ZeroArgs(Supplier function) implements Template { - InstantiatedTemplate instantiate() { + record ZeroArgs(Supplier function) implements Template { + TemplateBody instantiate() { return function.get(); } @@ -51,8 +51,8 @@ public TemplateWithArgs.ZeroArgsUse withArgs() { } } - record OneArg(String arg0Name, Function function) implements Template { - InstantiatedTemplate instantiate(A a) { + record OneArg(String arg0Name, Function function) implements Template { + TemplateBody instantiate(A a) { return function.apply(a); } @@ -62,8 +62,8 @@ public TemplateWithArgs.OneArgUse withArgs(A a) { } record TwoArgs(String arg0Name, String arg1Name, - BiFunction function) implements Template { - InstantiatedTemplate instantiate(A a, B b) { + BiFunction function) implements Template { + TemplateBody instantiate(A a, B b) { return function.apply(a, b); } @@ -72,8 +72,8 @@ public TemplateWithArgs.TwoArgsUse withArgs(A a, B b) { } } - static InstantiatedTemplate body(Object... tokens) { - return new InstantiatedTemplate(Token.parse(Arrays.asList(tokens))); + static TemplateBody body(Object... tokens) { + return new TemplateBody(Token.parse(Arrays.asList(tokens))); } static HookIntoToken intoHook(Hook hook, TemplateWithArgs t) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBody.java similarity index 95% rename from test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java rename to test/hotspot/jtreg/compiler/lib/template_framework/TemplateBody.java index 30d31ada287cc..9a0639e0c3d0a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/InstantiatedTemplate.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBody.java @@ -25,4 +25,4 @@ import java.util.List; -public record InstantiatedTemplate(List tokens) {} +public record TemplateBody(List tokens) {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java index 6f8c15f8aef95..4495acdc224a5 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java @@ -31,7 +31,7 @@ public sealed interface TemplateWithArgs extends Token { record ZeroArgsUse(Template.ZeroArgs zeroArgs) implements TemplateWithArgs, Token { @Override - public InstantiatedTemplate instantiate() { + public TemplateBody instantiate() { return zeroArgs.instantiate(); } @@ -41,7 +41,7 @@ public void visitArguments(ArgumentVisitor visitor) {} record OneArgUse(Template.OneArg oneArg, A a) implements TemplateWithArgs, Token { @Override - public InstantiatedTemplate instantiate() { + public TemplateBody instantiate() { return oneArg.instantiate(a); } @@ -53,7 +53,7 @@ public void visitArguments(ArgumentVisitor visitor) { record TwoArgsUse(Template.TwoArgs twoArgs, A a, B b) implements TemplateWithArgs, Token { @Override - public InstantiatedTemplate instantiate() { + public TemplateBody instantiate() { return twoArgs.instantiate(a, b); } @@ -64,7 +64,7 @@ public void visitArguments(ArgumentVisitor visitor) { } } - InstantiatedTemplate instantiate(); + TemplateBody instantiate(); @FunctionalInterface interface ArgumentVisitor { From 2b0e2a3d89476804f211e617e8510f7d5d25e844 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Feb 2025 12:05:50 +0100 Subject: [PATCH 029/212] change from LetToken to Nothing --- .../{LetToken.java => NothingToken.java} | 2 +- .../compiler/lib/template_framework/Renderer.java | 8 +++++--- .../compiler/lib/template_framework/Template.java | 13 +++++++++++-- .../compiler/lib/template_framework/Token.java | 2 +- 4 files changed, 18 insertions(+), 7 deletions(-) rename test/hotspot/jtreg/compiler/lib/template_framework/{LetToken.java => NothingToken.java} (94%) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/LetToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/NothingToken.java similarity index 94% rename from test/hotspot/jtreg/compiler/lib/template_framework/LetToken.java rename to test/hotspot/jtreg/compiler/lib/template_framework/NothingToken.java index 83221680dd9eb..540eaf1e14c9e 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/LetToken.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/NothingToken.java @@ -23,4 +23,4 @@ package compiler.lib.template_framework; -public record LetToken(String key, String value) implements Token {} +record NothingToken() implements Token {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 203998344b3cd..c7f464faaaacd 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -85,6 +85,10 @@ public static String render(TemplateWithArgs templateWithArgs) { return getCurrentTemplateFrame().$(name); } + static void addHashtagReplacement(String key, String value) { + getCurrentTemplateFrame().addHashtagReplacement(key, value); + } + // TODO //public static R let(String key, T value, Function block) { // getCurrentFrame().addContext(key, value.toString()); @@ -130,9 +134,7 @@ private static void renderToken(Token token) { CodeFrame codeFrame = getCurrentCodeFrame(); switch (token) { case StringToken(String s) -> codeFrame.addString(templateString(s)); - case LetToken(String key, String value) -> { - getCurrentTemplateFrame().addHashtagReplacement(key, value.toString()); - } + case NothingToken() -> { /* nothing */ } // TODO check order? case HookSetToken(Hook hook, List tokens) -> { // TODO describe and maybe rename to HookSetUse CodeFrame outerCodeFrame = getCurrentCodeFrame(); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 81c5305fc5afd..2f7893e6fe16f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -84,7 +84,16 @@ static HookIntoToken intoHook(Hook hook, TemplateWithArgs t) { return Renderer.$(name); } - static LetToken let(String key, Object value) { - return new LetToken(key, value.toString()); + static NothingToken let(String key, Object value) { + // TODO check order? + Renderer.addHashtagReplacement(key, value.toString()); + return new NothingToken(); } + + //static TemplateBody let(String key, T value, Function function) { + // // Idea: let all at the beginning, check for that. + // // directly add to hashtag replacement + // // let with function: after hashtag replacement is added, evaluate the body, pass it out. + // return new LetToken(key, value, function); + //} } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java index 80605ae674fba..8a6bcd92c665c 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java @@ -33,7 +33,7 @@ sealed interface Token permits StringToken, TemplateWithArgs.TwoArgsUse, HookSetToken, HookIntoToken, - LetToken + NothingToken { static List parse(Object object) { List outputList = new ArrayList(); From 41bc62c689a608ec5c5cb7b5850083d348d3815a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Feb 2025 14:05:55 +0100 Subject: [PATCH 030/212] let with function --- .../lib/template_framework/Template.java | 11 +++---- .../tests/TestTemplate.java | 31 +++++++------------ 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 2f7893e6fe16f..86b089d857488 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -90,10 +90,9 @@ static NothingToken let(String key, Object value) { return new NothingToken(); } - //static TemplateBody let(String key, T value, Function function) { - // // Idea: let all at the beginning, check for that. - // // directly add to hashtag replacement - // // let with function: after hashtag replacement is added, evaluate the body, pass it out. - // return new LetToken(key, value, function); - //} + static TemplateBody let(String key, T value, Function function) { + // TODO check order? + Renderer.addHashtagReplacement(key, value.toString()); + return function.apply(value); + } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 79869716723c1..f3376629c2a77 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -515,7 +515,14 @@ public static void testLet() { "}\n" )); - var template2 = Template.make(() -> body( + var template2 = Template.make("a", (Integer a) -> let("b", a * 10, b -> + body( + let("c", b * 3), + "abc = #a #b #c\n" + ) + )); + + var template3 = Template.make(() -> body( "{\n", let("x", "abc"), template1.withArgs("alpha"), @@ -529,33 +536,19 @@ public static void testLet() { ), "break\n", "y2 = #y\n", // leaks outside + "break\n", + template2.withArgs(5), "}\n" )); - String code = template2.withArgs().render(); + String code = template3.withArgs().render(); String expected = """ - { - { - y alpha y - y y - } - break - x1 = abc - x2 = abc - { - y beta y - y y - } - y1 = one - break - y2 = one - } + """; checkEQ(code, expected); } - public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { System.out.println("\"" + code + "\""); From 0bca0e61523b6ac004817573aac3ecc78ff41ea5 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Feb 2025 14:17:55 +0100 Subject: [PATCH 031/212] let example --- .../lib/template_framework/Renderer.java | 8 +------- .../lib/template_framework/Template.java | 2 -- .../tests/TestTemplate.java | 19 ++++++++++++++++++- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index c7f464faaaacd..c51d51e4325c8 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -89,12 +89,6 @@ static void addHashtagReplacement(String key, String value) { getCurrentTemplateFrame().addHashtagReplacement(key, value); } - // TODO - //public static R let(String key, T value, Function block) { - // getCurrentFrame().addContext(key, value.toString()); - // return block.apply(value); - //} - // TODO fuel - based on codeFrame or templateFrame? public static int depth() { return getCurrentCodeFrame().depth(); @@ -120,7 +114,7 @@ private static void renderTemplateWithArgs(TemplateWithArgs templateWithArgs) { TemplateFrame templateFrame = new TemplateFrame(getCurrentTemplateFrame(), nextTemplateId++); currentTemplateFrame = templateFrame; - templateWithArgs.visitArguments((name, value) -> templateFrame.addHashtagReplacement(name, value.toString())); + templateWithArgs.visitArguments((name, value) -> addHashtagReplacement(name, value.toString())); TemplateBody it = templateWithArgs.instantiate(); renderTokenList(it.tokens()); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 86b089d857488..19d8f7f0e1ed4 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -85,13 +85,11 @@ static HookIntoToken intoHook(Hook hook, TemplateWithArgs t) { } static NothingToken let(String key, Object value) { - // TODO check order? Renderer.addHashtagReplacement(key, value.toString()); return new NothingToken(); } static TemplateBody let(String key, T value, Function function) { - // TODO check order? Renderer.addHashtagReplacement(key, value.toString()); return function.apply(value); } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index f3376629c2a77..d78f0071e0a2a 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -544,7 +544,24 @@ public static void testLet() { String code = template3.withArgs().render(); String expected = """ - + { + { + y alpha y + y y + } + break + x1 = abc + x2 = abc + { + y beta y + y y + } + y1 = one + break + y2 = one + break + abc = 5 50 150 + } """; checkEQ(code, expected); } From 9b8e9f7a1da364d2c6345af028b95aeb66f189d9 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Feb 2025 14:53:01 +0100 Subject: [PATCH 032/212] Renderer refactor --- .../lib/template_framework/Renderer.java | 133 +++++++++--------- .../lib/template_framework/Template.java | 6 +- 2 files changed, 71 insertions(+), 68 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index c51d51e4325c8..b4f1e3c5cd761 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -30,34 +30,52 @@ import java.util.regex.Pattern; import java.util.stream.Stream; -public abstract class Renderer { +public class Renderer { private static final Pattern DOLLAR_NAME_PATTERN = Pattern.compile("\\$([a-zA-Z_][a-zA-Z0-9_]*)"); private static final Pattern HASHTAG_REPLACEMENT_PATTERN = Pattern.compile("#([a-zA-Z_][a-zA-Z0-9_]*)"); + /** + * There can be at most one Renderer instance at any time. This is to avoid that users accidentally + * render templates to strings, rather than letting them all render together. + */ + private static Renderer renderer = null; + // TODO describe - private static CodeFrame baseCodeFrame = null; - private static CodeFrame currentCodeFrame = null; + private int nextTemplateFrameId = 0; + private TemplateFrame baseTemplateFrame = new TemplateFrame(null, nextTemplateFrameId++); + private TemplateFrame currentTemplateFrame = baseTemplateFrame; + private CodeFrame baseCodeFrame = new CodeFrame(null); + private CodeFrame currentCodeFrame = baseCodeFrame; - private static int nextTemplateId = 0; // TODO refactor + // We do not want any other instances. + private Renderer() {} - static TemplateFrame baseTemplateFrame = null; - static TemplateFrame currentTemplateFrame = null; + static Renderer getCurrent() { + if (renderer == null) { + // TODO update text - which methods are involved? + throw new RendererException("A method such as $ or let was called outside a template rendering. Make sure you are not calling templates yourself, but use use()."); + } + return renderer; + } public static String render(TemplateWithArgs templateWithArgs) { // Check nobody else is using the Renderer. - if (baseCodeFrame != null) { + if (renderer != null) { throw new RendererException("Nested render not allowed."); } - // Setup the Renderer. - baseCodeFrame = new CodeFrame(null); - currentCodeFrame = baseCodeFrame; - nextTemplateId = 0; - baseTemplateFrame = new TemplateFrame(null, nextTemplateId++); - currentTemplateFrame = baseTemplateFrame; + renderer = new Renderer(); + renderer.renderTemplateWithArgs(templateWithArgs); + renderer.checkFrameConsistencyAfterRendering(); + String code = renderer.collectCode(); - renderTemplateWithArgs(templateWithArgs); + // Release the Renderer. + renderer = null; + return code; + } + + private void checkFrameConsistencyAfterRendering() { // Ensure CodeFrame consistency. if (baseCodeFrame != currentCodeFrame) { throw new RendererException("Renderer did not end up at base CodeFrame."); @@ -66,52 +84,33 @@ public static String render(TemplateWithArgs templateWithArgs) { if (baseTemplateFrame != currentTemplateFrame) { throw new RendererException("Renderer did not end up at base TemplateFrame."); } + } - // Collect Code to String. + private String collectCode() { StringBuilder builder = new StringBuilder(); baseCodeFrame.getCode().renderTo(builder); - String code = builder.toString(); - - // Release the Renderer. - baseCodeFrame = null; - currentCodeFrame = null; - baseTemplateFrame = null; - currentTemplateFrame = null; - - return code; - } - - static String $(String name) { - return getCurrentTemplateFrame().$(name); + return builder.toString(); } - static void addHashtagReplacement(String key, String value) { - getCurrentTemplateFrame().addHashtagReplacement(key, value); + String $(String name) { + return currentTemplateFrame.$(name); } - // TODO fuel - based on codeFrame or templateFrame? - public static int depth() { - return getCurrentCodeFrame().depth(); + void addHashtagReplacement(String key, String value) { + currentTemplateFrame.addHashtagReplacement(key, value); } - private static CodeFrame getCurrentCodeFrame() { - if (currentCodeFrame == null) { - // TODO update text - which methods are involved? - throw new RendererException("A method such as $ or let was called outside a template rendering. Make sure you are not calling templates yourself, but use use()."); - } - return currentCodeFrame; + private String getHashtagReplacement(String key) { + return currentTemplateFrame.getHashtagReplacement(key); } - private static TemplateFrame getCurrentTemplateFrame() { - if (currentTemplateFrame == null) { - // TODO update text - which methods are involved? - throw new RendererException("A method such as $ or let was called outside a template rendering. Make sure you are not calling templates yourself, but use use()."); - } - return currentTemplateFrame; + // TODO fuel - based on codeFrame or templateFrame? + int depth() { + return currentCodeFrame.depth(); } - private static void renderTemplateWithArgs(TemplateWithArgs templateWithArgs) { - TemplateFrame templateFrame = new TemplateFrame(getCurrentTemplateFrame(), nextTemplateId++); + private void renderTemplateWithArgs(TemplateWithArgs templateWithArgs) { + TemplateFrame templateFrame = new TemplateFrame(currentTemplateFrame, nextTemplateFrameId++); currentTemplateFrame = templateFrame; templateWithArgs.visitArguments((name, value) -> addHashtagReplacement(name, value.toString())); @@ -124,14 +123,17 @@ private static void renderTemplateWithArgs(TemplateWithArgs templateWithArgs) { currentTemplateFrame = currentTemplateFrame.parent; } - private static void renderToken(Token token) { - CodeFrame codeFrame = getCurrentCodeFrame(); + private void renderToken(Token token) { switch (token) { - case StringToken(String s) -> codeFrame.addString(templateString(s)); - case NothingToken() -> { /* nothing */ } // TODO check order? + case StringToken(String s) -> { + currentCodeFrame.addString(templateString(s)); + } + case NothingToken() -> { + // Nothing. + } case HookSetToken(Hook hook, List tokens) -> { // TODO describe and maybe rename to HookSetUse - CodeFrame outerCodeFrame = getCurrentCodeFrame(); + CodeFrame outerCodeFrame = currentCodeFrame; // We need a CodeFrame to which the hook can insert code. That way, name // definitions at the hook cannot excape the hookCodeFrame. @@ -153,8 +155,8 @@ case HookSetToken(Hook hook, List tokens) -> { currentCodeFrame.addCode(innerCodeFrame.getCode()); } case HookIntoToken(Hook hook, TemplateWithArgs t) -> { - // TODO describe and maybe rename to IntoHookUse // Switch to hook CodeFrame. + CodeFrame callerCodeFrame = currentCodeFrame; CodeFrame hookCodeFrame = codeFrameForHook(hook); // Use a transparent nested CodeFrame. We need a CodeFrame so that the code generated @@ -171,42 +173,43 @@ case HookIntoToken(Hook hook, TemplateWithArgs t) -> { hookCodeFrame.addCode(currentCodeFrame.getCode()); // Switch back from hook CodeFrame to caller CodeFrame. - currentCodeFrame = codeFrame; + currentCodeFrame = callerCodeFrame; } case TemplateWithArgs t -> { // Use a nested CodeFrame. - currentCodeFrame = new CodeFrame(codeFrame); + CodeFrame callerCodeFrame = currentCodeFrame; + currentCodeFrame = new CodeFrame(currentCodeFrame); renderTemplateWithArgs(t); - codeFrame.addCode(currentCodeFrame.getCode()); - currentCodeFrame = codeFrame; + callerCodeFrame.addCode(currentCodeFrame.getCode()); + currentCodeFrame = callerCodeFrame; } } } - private static void renderTokenList(List tokens) { - CodeFrame codeFrame = getCurrentCodeFrame(); + private void renderTokenList(List tokens) { + CodeFrame codeFrame = currentCodeFrame; for (Token t : tokens) { renderToken(t); } - if (codeFrame != getCurrentCodeFrame()) { + if (codeFrame != currentCodeFrame) { throw new RendererException("CodeFrame mismatch."); } } - private static String templateString(String s) { + private String templateString(String s) { var temp = DOLLAR_NAME_PATTERN.matcher(s).replaceAll( - (MatchResult result) -> getCurrentTemplateFrame().$(result.group(1)) + (MatchResult result) -> $(result.group(1)) ); return HASHTAG_REPLACEMENT_PATTERN.matcher(temp).replaceAll( // We must escape "$", because it has a special meaning in replaceAll. - (MatchResult result) -> getCurrentTemplateFrame().getHashtagReplacement(result.group(1)).replace("$", "\\$") + (MatchResult result) -> getHashtagReplacement(result.group(1)).replace("$", "\\$") ); } - private static CodeFrame codeFrameForHook(Hook hook) { - CodeFrame codeFrame = getCurrentCodeFrame(); + private CodeFrame codeFrameForHook(Hook hook) { + CodeFrame codeFrame = currentCodeFrame; while (codeFrame != null) { if (codeFrame.hasHook(hook)) { return codeFrame; diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 19d8f7f0e1ed4..fa23d25ea9530 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -81,16 +81,16 @@ static HookIntoToken intoHook(Hook hook, TemplateWithArgs t) { } static String $(String name) { - return Renderer.$(name); + return Renderer.getCurrent().$(name); } static NothingToken let(String key, Object value) { - Renderer.addHashtagReplacement(key, value.toString()); + Renderer.getCurrent().addHashtagReplacement(key, value.toString()); return new NothingToken(); } static TemplateBody let(String key, T value, Function function) { - Renderer.addHashtagReplacement(key, value.toString()); + Renderer.getCurrent().addHashtagReplacement(key, value.toString()); return function.apply(value); } } From 4be7d9813a1ed66a14fd4e7011e0676af110a2f3 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Feb 2025 15:05:23 +0100 Subject: [PATCH 033/212] testSelector --- .../tests/TestTemplate.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index d78f0071e0a2a..0d714f6e15e92 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -63,6 +63,7 @@ public static void main(String[] args) { testHookRecursion(); testNames(); testLet(); + testSelector(); //testClassInstantiator(); //testRepeat(); @@ -566,6 +567,76 @@ public static void testLet() { checkEQ(code, expected); } + public static void testSelector() { + var template1 = Template.make("a", (String a) -> body( + "<\n", + "x #a x\n", + ">\n" + )); + + var template2 = Template.make("a", (String a) -> body( + "<\n", + "y #a y\n", + ">\n" + )); + + var template3 = Template.make("a", (Integer a) -> body( + "[\n", + "z #a z\n", + // Select which template should be used: + a > 0 ? template1.withArgs("A_" + a) + : template2.withArgs("B_" + a), + "]\n" + )); + + var template4 = Template.make(() -> body( + "{\n", + template3.withArgs(-1), + "break\n", + template3.withArgs(0), + "break\n", + template3.withArgs(1), + "break\n", + template3.withArgs(2), + "}\n" + )); + + String code = template4.withArgs().render(); + String expected = + """ + { + [ + z -1 z + < + y B_-1 y + > + ] + break + [ + z 0 z + < + y B_0 y + > + ] + break + [ + z 1 z + < + x A_1 x + > + ] + break + [ + z 2 z + < + x A_2 x + > + ] + } + """; + checkEQ(code, expected); + } + public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { System.out.println("\"" + code + "\""); From 3e2153d6ebf0c479c31b6b07bce44d2c0f09816a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Feb 2025 16:21:42 +0100 Subject: [PATCH 034/212] wip fuel and done TemplateBinding --- .../lib/template_framework/CodeFrame.java | 6 -- .../lib/template_framework/Renderer.java | 31 +++++--- .../lib/template_framework/Template.java | 14 ++-- .../template_framework/TemplateBinding.java | 42 ++++++++++ .../lib/template_framework/TemplateFrame.java | 12 ++- .../template_framework/TemplateWithArgs.java | 5 +- .../lib/template_framework/Token.java | 2 +- .../tests/TestTemplate.java | 77 +++++++++++++++++-- 8 files changed, 156 insertions(+), 33 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/TemplateBinding.java diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java index bd8d25a0a0b70..1fa6491969c50 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java @@ -37,12 +37,6 @@ class CodeFrame { this.parent = parent; } - // TODO move depth to TemplateFrame - public int depth() { - if (parent == null) { return 0; } - return parent.depth() + 1; - } - void addString(String s) { codeList.add(new Code.Token(s)); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index b4f1e3c5cd761..6e41c9ad3e273 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -41,14 +41,20 @@ public class Renderer { private static Renderer renderer = null; // TODO describe - private int nextTemplateFrameId = 0; - private TemplateFrame baseTemplateFrame = new TemplateFrame(null, nextTemplateFrameId++); - private TemplateFrame currentTemplateFrame = baseTemplateFrame; - private CodeFrame baseCodeFrame = new CodeFrame(null); - private CodeFrame currentCodeFrame = baseCodeFrame; + private int nextTemplateFrameId; + private TemplateFrame baseTemplateFrame; + private TemplateFrame currentTemplateFrame; + private CodeFrame baseCodeFrame; + private CodeFrame currentCodeFrame; // We do not want any other instances. - private Renderer() {} + private Renderer(float fuel) { + nextTemplateFrameId = 0; + baseTemplateFrame = TemplateFrame.makeBase(nextTemplateFrameId++, fuel); + currentTemplateFrame = baseTemplateFrame; + baseCodeFrame = new CodeFrame(null); + currentCodeFrame = baseCodeFrame; + } static Renderer getCurrent() { if (renderer == null) { @@ -59,12 +65,16 @@ static Renderer getCurrent() { } public static String render(TemplateWithArgs templateWithArgs) { + return render(templateWithArgs, 1.0f); + } + + public static String render(TemplateWithArgs templateWithArgs, float fuel) { // Check nobody else is using the Renderer. if (renderer != null) { throw new RendererException("Nested render not allowed."); } - renderer = new Renderer(); + renderer = new Renderer(fuel); renderer.renderTemplateWithArgs(templateWithArgs); renderer.checkFrameConsistencyAfterRendering(); String code = renderer.collectCode(); @@ -104,13 +114,12 @@ private String getHashtagReplacement(String key) { return currentTemplateFrame.getHashtagReplacement(key); } - // TODO fuel - based on codeFrame or templateFrame? - int depth() { - return currentCodeFrame.depth(); + float fuel() { + return currentTemplateFrame.fuel; } private void renderTemplateWithArgs(TemplateWithArgs templateWithArgs) { - TemplateFrame templateFrame = new TemplateFrame(currentTemplateFrame, nextTemplateFrameId++); + TemplateFrame templateFrame = TemplateFrame.make(currentTemplateFrame, nextTemplateFrameId++, 0.1f); currentTemplateFrame = templateFrame; templateWithArgs.visitArguments((name, value) -> addHashtagReplacement(name, value.toString())); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index fa23d25ea9530..5044d24a20443 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -33,8 +33,8 @@ static ZeroArgs make(Supplier t) { return new ZeroArgs(t); } - static OneArg make(String arg0Name, Function t) { - return new OneArg<>(arg0Name, t); + static OneArgs make(String arg0Name, Function t) { + return new OneArgs<>(arg0Name, t); } static TwoArgs make(String arg0Name, String arg1Name, BiFunction t) { @@ -51,13 +51,13 @@ public TemplateWithArgs.ZeroArgsUse withArgs() { } } - record OneArg(String arg0Name, Function function) implements Template { + record OneArgs(String arg0Name, Function function) implements Template { TemplateBody instantiate(A a) { return function.apply(a); } - public TemplateWithArgs.OneArgUse withArgs(A a) { - return new TemplateWithArgs.OneArgUse<>(this, a); + public TemplateWithArgs.OneArgsUse withArgs(A a) { + return new TemplateWithArgs.OneArgsUse<>(this, a); } } @@ -93,4 +93,8 @@ static TemplateBody let(String key, T value, Function funct Renderer.getCurrent().addHashtagReplacement(key, value.toString()); return function.apply(value); } + + static float fuel() { + return Renderer.getCurrent().fuel(); + } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBinding.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBinding.java new file mode 100644 index 0000000000000..d3a1ad42e8f44 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBinding.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. + */ + +package compiler.lib.template_framework; + +public class TemplateBinding { + private T template = null; + + public T get() { + if (template == null) { + throw new RendererException("Cannot 'get' before 'bind'."); + } + return template; + } + + public void bind(T template) { + if (this.template != null) { + throw new RendererException("Duplicate 'bind' not allowed."); + } + this.template = template; + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java index 38d80e52cfea4..104b6d498cb34 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java @@ -51,10 +51,20 @@ class TemplateFrame { final TemplateFrame parent; final int id; final Map hashtagReplacements = new HashMap<>(); + final float fuel; - TemplateFrame(TemplateFrame parent, int id) { + public static TemplateFrame makeBase(int id, float fuel) { + return new TemplateFrame(null, id, fuel); + } + + public static TemplateFrame make(TemplateFrame parent, int id, float fuelCost) { + return new TemplateFrame(parent, id, parent.fuel - fuelCost); + } + + private TemplateFrame(TemplateFrame parent, int id, float fuel) { this.parent = parent; this.id = id; + this.fuel = fuel; } public String $(String name) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java index 4495acdc224a5..34c2c63992630 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java @@ -23,10 +23,9 @@ package compiler.lib.template_framework; -// TODO rename OneArgUse to OneArgsUse for consistency? public sealed interface TemplateWithArgs extends Token permits TemplateWithArgs.ZeroArgsUse, - TemplateWithArgs.OneArgUse, + TemplateWithArgs.OneArgsUse, TemplateWithArgs.TwoArgsUse { record ZeroArgsUse(Template.ZeroArgs zeroArgs) implements TemplateWithArgs, Token { @@ -39,7 +38,7 @@ public TemplateBody instantiate() { public void visitArguments(ArgumentVisitor visitor) {} } - record OneArgUse(Template.OneArg oneArg, A a) implements TemplateWithArgs, Token { + record OneArgsUse(Template.OneArgs oneArg, A a) implements TemplateWithArgs, Token { @Override public TemplateBody instantiate() { return oneArg.instantiate(a); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java index 8a6bcd92c665c..86d39c00fab68 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java @@ -29,7 +29,7 @@ sealed interface Token permits StringToken, TemplateWithArgs, TemplateWithArgs.ZeroArgsUse, - TemplateWithArgs.OneArgUse, + TemplateWithArgs.OneArgsUse, TemplateWithArgs.TwoArgsUse, HookSetToken, HookIntoToken, diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 0d714f6e15e92..c69f057d05290 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -46,6 +46,7 @@ import static compiler.lib.template_framework.Template.intoHook; import static compiler.lib.template_framework.Template.$; import static compiler.lib.template_framework.Template.let; +import static compiler.lib.template_framework.Template.fuel; public class TestTemplate { private static final Random RANDOM = Utils.getRandomInstance(); @@ -56,7 +57,7 @@ public static void main(String[] args) { testBodyTokens(); testWithOneArguments(); testWithTwoArguments(); - testRecursive(); + testNested(); testHookSimple(); testHookNested(); testHookWithNestedTemplates(); @@ -64,17 +65,15 @@ public static void main(String[] args) { testNames(); testLet(); testSelector(); + testRecursion(); + //testFuel(); //testClassInstantiator(); - //testRepeat(); - //testDispatch(); //testClassInstantiatorAndDispatch(); - //testChoose(); //testFieldsAndVariables(); //testFieldsAndVariablesDispatch(); //testIntCon(); //testLongCon(); - //testFuel(); //testRecursiveCalls(); } @@ -168,7 +167,7 @@ public static void testWithTwoArguments() { checkEQ(template4.withArgs(444, 555).render(), "start 444 555 end"); } - public static void testRecursive() { + public static void testNested() { var template1 = Template.make(() -> body("proton")); var template2 = Template.make("a1", "a2", (String a1, String a2) -> body( @@ -637,6 +636,72 @@ public static void testSelector() { checkEQ(code, expected); } + public static void testRecursion() { + // Binding allows use of template1 inside template1, via the Binding indirection. + var binding1 = new TemplateBinding>(); + + var template1 = Template.make("i", (Integer i) -> body( + "[ #i\n", + // We cannot yet use the template1 directly, as it is being defined. + // So we use binding1 instead. + i < 0 ? "done\n" : binding1.get().withArgs(i - 1), + "] #i\n" + )); + binding1.bind(template1); + + var template2 = Template.make(() -> body( + "{\n", + // Now, we can use template1 normally, as it is already defined. + template1.withArgs(3), + "}\n" + )); + + String code = template2.withArgs().render(); + String expected = + """ + { + [ 3 + [ 2 + [ 1 + [ 0 + [ -1 + done + ] -1 + ] 0 + ] 1 + ] 2 + ] 3 + } + """; + checkEQ(code, expected); + } + + //public static void testFuel() { + // var template1 = Template.make(() -> body( + // "<", fuel(),">\n" + // )); + + // var template2 = Template.make("i", (Integer i) -> body( + // "[ #i\n", + // template1.withArgs(), + // i < 0 ? "done" : template2.withArgs(i - 1), + // "] #i\n" + // )); + + // var template3 = Template.make(() -> body( + // "{\n", + // template2.withArgs(3), + // "}\n" + // )); + + // String code = template3.withArgs().render(); + // String expected = + // """ + + // """; + // checkEQ(code, expected); + //} + public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { System.out.println("\"" + code + "\""); From 5a7d118dbbc285c3926930991d2aa505ee94913f Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Feb 2025 16:55:40 +0100 Subject: [PATCH 035/212] custom fuel cost with example --- .../lib/template_framework/Renderer.java | 12 +- .../lib/template_framework/Template.java | 5 + .../lib/template_framework/TemplateFrame.java | 14 +- .../template_framework/TemplateWithArgs.java | 4 + .../tests/TestTemplate.java | 129 ++++++++++++++---- 5 files changed, 130 insertions(+), 34 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 6e41c9ad3e273..77ad22f3bb118 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -64,11 +64,11 @@ static Renderer getCurrent() { return renderer; } - public static String render(TemplateWithArgs templateWithArgs) { - return render(templateWithArgs, 1.0f); + static String render(TemplateWithArgs templateWithArgs) { + return render(templateWithArgs, 100.0f); } - public static String render(TemplateWithArgs templateWithArgs, float fuel) { + static String render(TemplateWithArgs templateWithArgs, float fuel) { // Check nobody else is using the Renderer. if (renderer != null) { throw new RendererException("Nested render not allowed."); @@ -118,8 +118,12 @@ float fuel() { return currentTemplateFrame.fuel; } + void setFuelCost(float fuelCost) { + currentTemplateFrame.setFuelCost(fuelCost); + } + private void renderTemplateWithArgs(TemplateWithArgs templateWithArgs) { - TemplateFrame templateFrame = TemplateFrame.make(currentTemplateFrame, nextTemplateFrameId++, 0.1f); + TemplateFrame templateFrame = TemplateFrame.make(currentTemplateFrame, nextTemplateFrameId++); currentTemplateFrame = templateFrame; templateWithArgs.visitArguments((name, value) -> addHashtagReplacement(name, value.toString())); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 5044d24a20443..fdd0869859ca0 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -97,4 +97,9 @@ static TemplateBody let(String key, T value, Function funct static float fuel() { return Renderer.getCurrent().fuel(); } + + static NothingToken setFuelCost(float fuelCost) { + Renderer.getCurrent().setFuelCost(fuelCost); + return new NothingToken(); + } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java index 104b6d498cb34..abcb8f417cab2 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java @@ -52,19 +52,21 @@ class TemplateFrame { final int id; final Map hashtagReplacements = new HashMap<>(); final float fuel; + float fuelCost; public static TemplateFrame makeBase(int id, float fuel) { - return new TemplateFrame(null, id, fuel); + return new TemplateFrame(null, id, fuel, 0.0f); } - public static TemplateFrame make(TemplateFrame parent, int id, float fuelCost) { - return new TemplateFrame(parent, id, parent.fuel - fuelCost); + public static TemplateFrame make(TemplateFrame parent, int id) { + return new TemplateFrame(parent, id, parent.fuel - parent.fuelCost, 10.0f); } - private TemplateFrame(TemplateFrame parent, int id, float fuel) { + private TemplateFrame(TemplateFrame parent, int id, float fuel, float fuelCost) { this.parent = parent; this.id = id; this.fuel = fuel; + this.fuelCost = fuelCost; } public String $(String name) { @@ -85,4 +87,8 @@ String getHashtagReplacement(String key) { } throw new RendererException("Missing hashtag replacement for #" + key); } + + void setFuelCost(float fuelCost) { + this.fuelCost = fuelCost; + } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java index 34c2c63992630..d4c4e6585af70 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java @@ -79,4 +79,8 @@ interface ArgumentVisitor { default String render() { return Renderer.render(this); } + + default String render(float fuel) { + return Renderer.render(this, fuel); + } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index c69f057d05290..75de208572ad7 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -47,6 +47,7 @@ import static compiler.lib.template_framework.Template.$; import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.fuel; +import static compiler.lib.template_framework.Template.setFuelCost; public class TestTemplate { private static final Random RANDOM = Utils.getRandomInstance(); @@ -66,7 +67,8 @@ public static void main(String[] args) { testLet(); testSelector(); testRecursion(); - //testFuel(); + testFuel(); + testFuelCustom(); //testClassInstantiator(); //testClassInstantiatorAndDispatch(); @@ -676,31 +678,106 @@ public static void testRecursion() { checkEQ(code, expected); } - //public static void testFuel() { - // var template1 = Template.make(() -> body( - // "<", fuel(),">\n" - // )); - - // var template2 = Template.make("i", (Integer i) -> body( - // "[ #i\n", - // template1.withArgs(), - // i < 0 ? "done" : template2.withArgs(i - 1), - // "] #i\n" - // )); - - // var template3 = Template.make(() -> body( - // "{\n", - // template2.withArgs(3), - // "}\n" - // )); - - // String code = template3.withArgs().render(); - // String expected = - // """ - - // """; - // checkEQ(code, expected); - //} + public static void testFuel() { + var template1 = Template.make(() -> body( + let("f", fuel()), + + "<#f>\n" + )); + + // Binding allows use of template2 inside template2, via the Binding indirection. + var binding2 = new TemplateBinding>(); + var template2 = Template.make("i", (Integer i) -> body( + let("f", fuel()), + + "[ #i #f\n", + template1.withArgs(), + fuel() <= 60.f ? "done" : binding2.get().withArgs(i - 1), + "] #i #f\n" + )); + binding2.bind(template2); + + var template3 = Template.make(() -> body( + "{\n", + template2.withArgs(3), + "}\n" + )); + + String code = template3.withArgs().render(); + String expected = + """ + { + [ 3 90.0 + <80.0> + [ 2 80.0 + <70.0> + [ 1 70.0 + <60.0> + [ 0 60.0 + <50.0> + done] 0 60.0 + ] 1 70.0 + ] 2 80.0 + ] 3 90.0 + } + """; + checkEQ(code, expected); + } + + public static void testFuelCustom() { + var template1 = Template.make(() -> body( + setFuelCost(2.0f), + let("f", fuel()), + + "<#f>\n" + )); + + // Binding allows use of template2 inside template2, via the Binding indirection. + var binding2 = new TemplateBinding>(); + var template2 = Template.make("i", (Integer i) -> body( + setFuelCost(3.0f), + let("f", fuel()), + + "[ #i #f\n", + template1.withArgs(), + fuel() <= 5.f ? "done\n" : binding2.get().withArgs(i - 1), + "] #i #f\n" + )); + binding2.bind(template2); + + var template3 = Template.make(() -> body( + setFuelCost(5.0f), + let("f", fuel()), + + "{ #f\n", + template2.withArgs(3), + "} #f\n" + )); + + String code = template3.withArgs().render(20.0f); + String expected = + """ + { 20.0 + [ 3 15.0 + <12.0> + [ 2 12.0 + <9.0> + [ 1 9.0 + <6.0> + [ 0 6.0 + <3.0> + [ -1 3.0 + <0.0> + done + ] -1 3.0 + ] 0 6.0 + ] 1 9.0 + ] 2 12.0 + ] 3 15.0 + } 20.0 + """; + checkEQ(code, expected); + } public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { From 81beb03f932f59e3f7aef4cb2791e458ec0f9f09 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Feb 2025 17:01:10 +0100 Subject: [PATCH 036/212] imporve documentation --- .../lib/template_framework/Renderer.java | 1 - .../lib/template_framework/TemplateFrame.java | 17 +++-------------- .../template_framework/TemplateWithArgs.java | 4 ---- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 77ad22f3bb118..c0f0f63d3456f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -145,7 +145,6 @@ case NothingToken() -> { // Nothing. } case HookSetToken(Hook hook, List tokens) -> { - // TODO describe and maybe rename to HookSetUse CodeFrame outerCodeFrame = currentCodeFrame; // We need a CodeFrame to which the hook can insert code. That way, name diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java index abcb8f417cab2..820c57d3baf53 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java @@ -26,26 +26,15 @@ import java.util.HashMap; import java.util.Map; -// TODO remove / move elsewhere -// We need frames for -// - Variables: -// - normal template borders are opaque, but not for intoHook -// - hook scopes are also opaque -// -// - hashtag replacement: -// - template border opaque -// - hook scope transparent -// -// - Parent: -// - via calls -// - code / variable scopes - /** * The {@link TemplateFrame} is the frame for a {@link TemplateWithArgs}. It ensures * that each template use has its own unique {@link id} used to deconflict names * using {@link $}. It also has a set of hashtag replacements, which combine the * key-value pairs from the template argument and the {@link let} definitions. * The {@link parent} relationship provides a trace for the use chain of templates. + * The {@link fuel} is reduced over this chain, to give a heuristic on how much time + * is spend on the code from the template corrsponding to the frame, and to give a + * termination criterion to avoid nesting templates too deeply. */ class TemplateFrame { final TemplateFrame parent; diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java index d4c4e6585af70..927c0b8f3670a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java @@ -72,10 +72,6 @@ interface ArgumentVisitor { void visitArguments(ArgumentVisitor visitor); - // TODO ensure that there can be no nested rendering! - // Otherwise people might fold recursive to String uses too soon... true? - // Yes, the issue is that outer scope may create vars, the inner then does - // not have access! default String render() { return Renderer.render(this); } From 5daa95ef258bab309bd6bf01412d27ba4fbab135 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Feb 2025 17:09:09 +0100 Subject: [PATCH 037/212] create nice constants for fuel --- .../jtreg/compiler/lib/template_framework/Renderer.java | 2 +- .../jtreg/compiler/lib/template_framework/TemplateFrame.java | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index c0f0f63d3456f..bd82fe745b2c9 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -65,7 +65,7 @@ static Renderer getCurrent() { } static String render(TemplateWithArgs templateWithArgs) { - return render(templateWithArgs, 100.0f); + return render(templateWithArgs, TemplateFrame.DEFAULT_FUEL); } static String render(TemplateWithArgs templateWithArgs, float fuel) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java index 820c57d3baf53..6ca7838a3a04b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java @@ -37,6 +37,9 @@ * termination criterion to avoid nesting templates too deeply. */ class TemplateFrame { + final static float DEFAULT_FUEL = 100.0f; + final static float DEFAULT_FUEL_COST = 10.0f; + final TemplateFrame parent; final int id; final Map hashtagReplacements = new HashMap<>(); @@ -48,7 +51,7 @@ public static TemplateFrame makeBase(int id, float fuel) { } public static TemplateFrame make(TemplateFrame parent, int id) { - return new TemplateFrame(parent, id, parent.fuel - parent.fuelCost, 10.0f); + return new TemplateFrame(parent, id, parent.fuel - parent.fuelCost, DEFAULT_FUEL_COST); } private TemplateFrame(TemplateFrame parent, int id, float fuel, float fuelCost) { From 36fa57cb9a7c23c81dff980f748ed21f665bffbc Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Feb 2025 18:15:18 +0100 Subject: [PATCH 038/212] NameSet --- .../lib/template_framework/CodeFrame.java | 25 ++++++ .../lib/template_framework/NameSelection.java | 26 ++++++ .../lib/template_framework/NameSet.java | 86 +++++++++++++++++++ .../lib/template_framework/Renderer.java | 17 ++++ .../lib/template_framework/Template.java | 16 ++++ 5 files changed, 170 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/NameSelection.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java index 1fa6491969c50..07d119d9119ae 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java @@ -33,8 +33,13 @@ class CodeFrame { private final List codeList = new ArrayList(); private final Map hookCodeLists = new HashMap<>(); + final NameSet mutableNames; + final NameSet allNames; + CodeFrame(CodeFrame parent) { this.parent = parent; + this.mutableNames = new NameSet(parent != null ? parent.mutableNames : null); + this.allNames = new NameSet(parent != null ? parent.allNames : null); } void addString(String s) { @@ -56,6 +61,26 @@ boolean hasHook(Hook hook) { return hookCodeLists.containsKey(hook); } + private NameSet nameSet(NameSelection nameSelection) { + if (nameSelection == NameSelection.MUTABLE) { + return mutableNames; + } else { + return allNames; + } + } + + void defineName(String name, Object type, NameSelection nameSelection) { + nameSet(nameSelection).add(name, type); + } + + boolean hasNameFor(Object type, NameSelection nameSelection) { + return nameSet(nameSelection).count(type) > 0; + } + + String sampleName(Object type, NameSelection nameSelection) { + return nameSet(nameSelection).sample(type); + } + // TODO ensure only use once! Code getCode() { return new Code.CodeList(codeList); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/NameSelection.java b/test/hotspot/jtreg/compiler/lib/template_framework/NameSelection.java new file mode 100644 index 0000000000000..740ce208727c8 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/NameSelection.java @@ -0,0 +1,26 @@ +/* + * 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; + +enum NameSelection { MUTABLE, ALL } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java b/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java new file mode 100644 index 0000000000000..d2055f9e97c71 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java @@ -0,0 +1,86 @@ +/* + * 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; + +import java.util.HashMap; +import java.util.Map; +import java.util.ArrayList; +import java.util.List; + +class NameSet { + private final NameSet parent; + private final Map> names = new HashMap<>(); + + NameSet(NameSet parent) { + this.parent = parent; + } + + public int countLocal(Object type) { + List locals = names.get(type); + return (locals == null) ? 0 : locals.size(); + } + + public int count(Object type) { + int c = countLocal(type); + if (parent != null) { c += parent.count(type); } + return c; + } + + /** + * Randomly sample a name from this set or a parent set, restricted to the specified type. + */ + public String sample(Object type) { + int c = count(type); + if (c == 0) { + // No variable of this type + return null; + } + + // Maybe sample from parent. + if (parent != null) { + int pc = parent.count(type); + int r = Renderer.RANDOM.nextInt(c); + if (r < pc) { + return parent.sample(type); + } + } + + List locals = names.get(type); + int r = Renderer.RANDOM.nextInt(locals.size()); + return locals.get(r); + } + + /** + * Add a variable of a specified type to the set. + */ + public void add(String name, Object type) { + // Fetch list of variables - if non-existant create a new one. + List locals = names.get(type); + if (locals == null) { + locals = new ArrayList(); + names.put(type, locals); + } + locals.add(name); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index bd82fe745b2c9..3bb93cde621df 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -29,11 +29,16 @@ import java.util.regex.MatchResult; import java.util.regex.Pattern; import java.util.stream.Stream; +import java.util.Random; + +import jdk.test.lib.Utils; public class Renderer { private static final Pattern DOLLAR_NAME_PATTERN = Pattern.compile("\\$([a-zA-Z_][a-zA-Z0-9_]*)"); private static final Pattern HASHTAG_REPLACEMENT_PATTERN = Pattern.compile("#([a-zA-Z_][a-zA-Z0-9_]*)"); + static final Random RANDOM = Utils.getRandomInstance(); + /** * There can be at most one Renderer instance at any time. This is to avoid that users accidentally * render templates to strings, rather than letting them all render together. @@ -122,6 +127,18 @@ void setFuelCost(float fuelCost) { currentTemplateFrame.setFuelCost(fuelCost); } + void defineName(String name, Object type, NameSelection nameSelection) { + currentCodeFrame.defineName(name, type, nameSelection); + } + + boolean hasNameFor(Object type, NameSelection nameSelection) { + return currentCodeFrame.hasNameFor(type, nameSelection); + } + + String sampleName(Object type, NameSelection nameSelection) { + return currentCodeFrame.sampleName(type, nameSelection); + } + private void renderTemplateWithArgs(TemplateWithArgs templateWithArgs) { TemplateFrame templateFrame = TemplateFrame.make(currentTemplateFrame, nextTemplateFrameId++); currentTemplateFrame = templateFrame; diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index fdd0869859ca0..69ce009bb9b9a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -102,4 +102,20 @@ static NothingToken setFuelCost(float fuelCost) { Renderer.getCurrent().setFuelCost(fuelCost); return new NothingToken(); } + + static final NameSelection MUTABLE = NameSelection.MUTABLE; + static final NameSelection ALL = NameSelection.ALL; + + static NothingToken defineName(String name, Object type, NameSelection nameSelection) { + Renderer.getCurrent().defineName(name, type, nameSelection); + return new NothingToken(); + } + + static boolean hasNameFor(Object type, NameSelection nameSelection) { + return Renderer.getCurrent().hasNameFor(type, nameSelection); + } + + static String sampleName(Object type, NameSelection nameSelection) { + return Renderer.getCurrent().sampleName(type, nameSelection); + } } From 124213ae58bb9e85243f3b8c8478e572ce04ed9c Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Feb 2025 11:15:26 +0100 Subject: [PATCH 039/212] testNames wip --- .../lib/template_framework/CodeFrame.java | 4 +- .../lib/template_framework/Renderer.java | 4 +- .../lib/template_framework/Template.java | 4 +- .../tests/TestTemplate.java | 51 ++++++++++++++++++- 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java index 07d119d9119ae..648a10beb34b5 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java @@ -73,8 +73,8 @@ void defineName(String name, Object type, NameSelection nameSelection) { nameSet(nameSelection).add(name, type); } - boolean hasNameFor(Object type, NameSelection nameSelection) { - return nameSet(nameSelection).count(type) > 0; + int countNames(Object type, NameSelection nameSelection) { + return nameSet(nameSelection).count(type); } String sampleName(Object type, NameSelection nameSelection) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 3bb93cde621df..955c4c605eec4 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -131,8 +131,8 @@ void defineName(String name, Object type, NameSelection nameSelection) { currentCodeFrame.defineName(name, type, nameSelection); } - boolean hasNameFor(Object type, NameSelection nameSelection) { - return currentCodeFrame.hasNameFor(type, nameSelection); + int countNames(Object type, NameSelection nameSelection) { + return currentCodeFrame.countNames(type, nameSelection); } String sampleName(Object type, NameSelection nameSelection) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 69ce009bb9b9a..12a9f81c8e6ff 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -111,8 +111,8 @@ static NothingToken defineName(String name, Object type, NameSelection nameSelec return new NothingToken(); } - static boolean hasNameFor(Object type, NameSelection nameSelection) { - return Renderer.getCurrent().hasNameFor(type, nameSelection); + static int countNames(Object type, NameSelection nameSelection) { + return Renderer.getCurrent().countNames(type, nameSelection); } static String sampleName(Object type, NameSelection nameSelection) { diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 75de208572ad7..a6afd259db6da 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -48,6 +48,11 @@ import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.fuel; import static compiler.lib.template_framework.Template.setFuelCost; +import static compiler.lib.template_framework.Template.defineName; +import static compiler.lib.template_framework.Template.countNames; +import static compiler.lib.template_framework.Template.sampleName; +import static compiler.lib.template_framework.Template.MUTABLE; +import static compiler.lib.template_framework.Template.ALL; public class TestTemplate { private static final Random RANDOM = Utils.getRandomInstance(); @@ -63,12 +68,13 @@ public static void main(String[] args) { testHookNested(); testHookWithNestedTemplates(); testHookRecursion(); - testNames(); + testDollar(); testLet(); testSelector(); testRecursion(); testFuel(); testFuelCustom(); + testNames(); //testClassInstantiator(); //testClassInstantiatorAndDispatch(); @@ -441,7 +447,7 @@ public static void testHookRecursion() { checkEQ(code, expected); } - public static void testNames() { + public static void testDollar() { var hook1 = new Hook("Hook1"); var template1 = Template.make("a", (String a) -> body("x $name #a x\n")); @@ -779,6 +785,47 @@ public static void testFuelCustom() { checkEQ(code, expected); } + public static void testNames() { + var hook1 = new Hook("Hook1"); + + var template1 = Template.make("name", "type", (String name, Object type) -> body( + defineName(name, type, MUTABLE), + "define #type #name\n" + )); + + var template2 = Template.make(() -> body( + "<\n", + intoHook(hook1, template1.withArgs($("name"), "int")), + "$name = 5", + ">\n" + )); + + var template3 = Template.make(() -> body( + "[", countNames("int", MUTABLE), "]\n" + )); + + var template4 = Template.make(() -> body( + "{\n", + template3.withArgs(), + hook1.set( + template3.withArgs(), + "something\n", + template2.withArgs(), + "more\n", + template3.withArgs() + ), + template3.withArgs(), + "}\n" + )); + + String code = template4.withArgs().render(); + String expected = + """ + + """; + checkEQ(code, expected); + } + public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { System.out.println("\"" + code + "\""); From b2e72815e98b3a87ccc5de7a6a845dbfdf7ba870 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Feb 2025 11:52:27 +0100 Subject: [PATCH 040/212] testNames / defineName first example working --- .../lib/template_framework/CodeFrame.java | 29 +++++++++-- .../lib/template_framework/Renderer.java | 11 ++-- .../tests/TestTemplate.java | 52 +++++++++++++------ 3 files changed, 68 insertions(+), 24 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java index 648a10beb34b5..03dd199e56d19 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java @@ -36,10 +36,33 @@ class CodeFrame { final NameSet mutableNames; final NameSet allNames; - CodeFrame(CodeFrame parent) { + private CodeFrame(CodeFrame parent, boolean isTransparentForNames) { this.parent = parent; - this.mutableNames = new NameSet(parent != null ? parent.mutableNames : null); - this.allNames = new NameSet(parent != null ? parent.allNames : null); + if (parent == null) { + // NameSets without any parent. + this.mutableNames = new NameSet(null); + this.allNames = new NameSet(null); + } else if (isTransparentForNames) { + // We use the same NameSets as the parent - makes it transparent. + this.mutableNames = parent.mutableNames; + this.allNames = parent.allNames; + } else { + // New NameSets, to make sure we have a nested scope for the names. + this.mutableNames = new NameSet(parent.mutableNames); + this.allNames = new NameSet(parent.allNames); + } + } + + public static CodeFrame makeBase() { + return new CodeFrame(null, false); + } + + public static CodeFrame make(CodeFrame parent) { + return new CodeFrame(parent, false); + } + + public static CodeFrame makeTransparentForNames(CodeFrame parent) { + return new CodeFrame(parent, true); } void addString(String s) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 955c4c605eec4..5c4beb8f89d0b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -57,7 +57,7 @@ private Renderer(float fuel) { nextTemplateFrameId = 0; baseTemplateFrame = TemplateFrame.makeBase(nextTemplateFrameId++, fuel); currentTemplateFrame = baseTemplateFrame; - baseCodeFrame = new CodeFrame(null); + baseCodeFrame = CodeFrame.makeBase(); currentCodeFrame = baseCodeFrame; } @@ -166,13 +166,13 @@ case HookSetToken(Hook hook, List tokens) -> { // We need a CodeFrame to which the hook can insert code. That way, name // definitions at the hook cannot excape the hookCodeFrame. - CodeFrame hookCodeFrame = new CodeFrame(outerCodeFrame); + CodeFrame hookCodeFrame = CodeFrame.make(outerCodeFrame); hookCodeFrame.addHook(hook); // We need a CodeFrame where the tokens can be rendered. That way, name // definitions from the tokens cannot escape the innerCodeFrame to the // hookCodeFrame. - CodeFrame innerCodeFrame = new CodeFrame(hookCodeFrame); + CodeFrame innerCodeFrame = CodeFrame.make(hookCodeFrame); currentCodeFrame = innerCodeFrame; renderTokenList(tokens); @@ -194,8 +194,7 @@ case HookIntoToken(Hook hook, TemplateWithArgs t) -> { // the hookCodeFrame. // But the CodeFrame must be transparent, so that its name definitions go out to // the hookCodeFrame, and are not limited to the CodeFrame for the TemplateWithArgs. - currentCodeFrame = new CodeFrame(hookCodeFrame); - // TODO make transparent for names + currentCodeFrame = CodeFrame.makeTransparentForNames(hookCodeFrame); renderTemplateWithArgs(t); @@ -207,7 +206,7 @@ case HookIntoToken(Hook hook, TemplateWithArgs t) -> { case TemplateWithArgs t -> { // Use a nested CodeFrame. CodeFrame callerCodeFrame = currentCodeFrame; - currentCodeFrame = new CodeFrame(currentCodeFrame); + currentCodeFrame = CodeFrame.make(currentCodeFrame); renderTemplateWithArgs(t); diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index a6afd259db6da..994e2d92d3650 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -788,40 +788,62 @@ public static void testFuelCustom() { public static void testNames() { var hook1 = new Hook("Hook1"); - var template1 = Template.make("name", "type", (String name, Object type) -> body( - defineName(name, type, MUTABLE), - "define #type #name\n" + var template1 = Template.make(() -> body( + "[", countNames("int", MUTABLE), "]\n" )); - var template2 = Template.make(() -> body( - "<\n", - intoHook(hook1, template1.withArgs($("name"), "int")), - "$name = 5", - ">\n" + var template2 = Template.make("name", "type", (String name, Object type) -> body( + defineName(name, type, MUTABLE), + "define #type #name\n", + template1.withArgs() )); var template3 = Template.make(() -> body( - "[", countNames("int", MUTABLE), "]\n" + "<\n", + intoHook(hook1, template2.withArgs($("name"), "int")), + "$name = 5\n", + ">\n" )); var template4 = Template.make(() -> body( "{\n", - template3.withArgs(), + template1.withArgs(), hook1.set( - template3.withArgs(), + template1.withArgs(), "something\n", - template2.withArgs(), + template3.withArgs(), + "more\n", + template1.withArgs(), "more\n", - template3.withArgs() + template2.withArgs($("name"), "int"), + "more\n", + template1.withArgs() ), - template3.withArgs(), + template1.withArgs(), "}\n" )); String code = template4.withArgs().render(); String expected = """ - + { + [0] + define int name_4 + [1] + [0] + something + < + name_4 = 5 + > + more + [1] + more + define int name_1 + [2] + more + [1] + [0] + } """; checkEQ(code, expected); } From 4698e5fe7949a992dccc12d1841407408037e11d Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Feb 2025 12:30:58 +0100 Subject: [PATCH 041/212] testNames2 --- .../lib/template_framework/NameSet.java | 3 +- .../tests/TestTemplate.java | 105 ++++++++++++++++++ 2 files changed, 106 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java b/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java index d2055f9e97c71..04b472d726787 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java @@ -53,8 +53,7 @@ public int count(Object type) { public String sample(Object type) { int c = count(type); if (c == 0) { - // No variable of this type - return null; + throw new RendererException("No variable of type " + type.toString()); } // Maybe sample from parent. diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 994e2d92d3650..bd3eeb80da51e 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -75,6 +75,7 @@ public static void main(String[] args) { testFuel(); testFuelCustom(); testNames(); + testNames2(); //testClassInstantiator(); //testClassInstantiatorAndDispatch(); @@ -848,6 +849,110 @@ public static void testNames() { checkEQ(code, expected); } + public static void testNames2() { + var hook1 = new Hook("Hook1"); + + var template1 = Template.make("type", (Object type) -> body( + "[#type: ", countNames(type, MUTABLE), " and ", countNames(type, ALL), "]\n" + )); + + + var template2 = Template.make("name", "type", (String name, Object type) -> body( + defineName(name, type, MUTABLE), + "define mutable #type #name\n", + template1.withArgs(type) + )); + + var template3 = Template.make("name", "type", (String name, Object type) -> body( + defineName(name, type, ALL), + "define immutable #type #name\n", + template1.withArgs(type) + )); + + var template4 = Template.make("type", (Object type) -> body( + "{ $store\n", + intoHook(hook1, template2.withArgs($("name"), type)), + "$name = 5\n", + "} $store\n" + )); + + var template5 = Template.make("type", (Object type) -> body( + "{ $load\n", + intoHook(hook1, template3.withArgs($("name"), type)), + "blackhole($name)\n", + "} $load\n" + )); + + var template6 = Template.make("type", (Object type) -> body( + let("v", sampleName(type, MUTABLE)), + "{ $sample\n", + "#v = 7\n", + "} $sample\n" + )); + + var template7 = Template.make("type", (Object type) -> body( + let("v", sampleName(type, ALL)), + "{ $sample\n", + "blackhole(#v)\n", + "} $sample\n" + )); + + var template8 = Template.make(() -> body( + "class $X {\n", + template1.withArgs("int"), + hook1.set( + "begin $body\n", + template1.withArgs("int"), + "start with immutable\n", + template5.withArgs("int"), + "then load from it\n", + template7.withArgs("int"), + template1.withArgs("int"), + "now make something mutable\n", + template4.withArgs("int"), + "then store to it\n", + template6.withArgs("int"), + template1.withArgs("int") + ), + template1.withArgs("int"), + "}\n" + )); + + String code = template8.withArgs().render(); + String expected = + """ + class X_1 { + [int: 0 and 0] + define immutable int name_4 + [int: 0 and 1] + define mutable int name_9 + [int: 1 and 1] + begin body_1 + [int: 0 and 0] + start with immutable + { load_4 + blackhole(name_4) + } load_4 + then load from it + { sample_7 + blackhole(name_4) + } sample_7 + [int: 0 and 1] + now make something mutable + { store_9 + name_9 = 5 + } store_9 + then store to it + { sample_12 + name_9 = 7 + } sample_12 + [int: 1 and 1] + [int: 0 and 0] + } + """; + checkEQ(code, expected); + } + public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { System.out.println("\"" + code + "\""); From 6565068eafd69e1bba9d4213376d885506cd129b Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Feb 2025 12:46:18 +0100 Subject: [PATCH 042/212] testNames3 --- .../tests/TestTemplate.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index bd3eeb80da51e..b3c756407ec3a 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -76,6 +76,7 @@ public static void main(String[] args) { testFuelCustom(); testNames(); testNames2(); + testNames3(); //testClassInstantiator(); //testClassInstantiatorAndDispatch(); @@ -953,6 +954,48 @@ class X_1 { checkEQ(code, expected); } + public static void testNames3() { + var hook1 = new Hook("Hook1"); + + var template1 = Template.make("type", (Object type) -> body( + "[#type: ", countNames(type, MUTABLE), " and ", countNames(type, ALL), "]\n" + )); + + // Example that shows that defineName runs before any code gets generated. + var template2 = Template.make(() -> body( + "class $Y {\n", + template1.withArgs("int"), + hook1.set( + "begin $body\n", + template1.withArgs("int"), + "define mutable\n", + defineName($("v1"), "int", MUTABLE), + template1.withArgs("int"), + "define immutable\n", + defineName($("v1"), "int", ALL), + template1.withArgs("int") + ), + template1.withArgs("int"), + "}\n" + )); + + String code = template2.withArgs().render(); + String expected = + """ + class Y_1 { + [int: 1 and 1] + begin body_1 + [int: 1 and 1] + define mutable + [int: 1 and 1] + define immutable + [int: 1 and 1] + [int: 1 and 1] + } + """; + checkEQ(code, expected); + } + public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { System.out.println("\"" + code + "\""); From a8e925f2ccd56a60ffee68dd7d6c2cd87e25b2ad Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Feb 2025 13:06:29 +0100 Subject: [PATCH 043/212] testListArgument --- .../tests/TestTemplate.java | 52 ++++++++++++++++--- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index b3c756407ec3a..d6f421282bafc 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -77,14 +77,7 @@ public static void main(String[] args) { testNames(); testNames2(); testNames3(); - - //testClassInstantiator(); - //testClassInstantiatorAndDispatch(); - //testFieldsAndVariables(); - //testFieldsAndVariablesDispatch(); - //testIntCon(); - //testLongCon(); - //testRecursiveCalls(); + testListArgument(); } public static void testSingleLine() { @@ -962,6 +955,7 @@ public static void testNames3() { )); // Example that shows that defineName runs before any code gets generated. + // To avoid this behaviour, you have to wrap the defineName in their own template. var template2 = Template.make(() -> body( "class $Y {\n", template1.withArgs("int"), @@ -996,6 +990,48 @@ class Y_1 { checkEQ(code, expected); } + record MyItem(Object type, String op) {} + + public static void testListArgument() { + var template1 = Template.make("item", (MyItem item) -> body( + let("type", item.type()), + let("op", item.op()), + "#type apply #op\n" + )); + + var template2 = Template.make("list", (List list) -> body( + "class $Z {\n", + // Use template1 for every item in the list. + list.stream().map(item -> template1.withArgs(item)).toList(), + "}\n" + )); + + List list = List.of(new MyItem("int", "+"), + new MyItem("int", "-"), + new MyItem("int", "*"), + new MyItem("int", "/"), + new MyItem("long", "+"), + new MyItem("long", "-"), + new MyItem("long", "*"), + new MyItem("long", "/")); + + String code = template2.withArgs(list).render(); + String expected = + """ + class Z_1 { + int apply + + int apply - + int apply * + int apply / + long apply + + long apply - + long apply * + long apply / + } + """; + checkEQ(code, expected); + } + public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { System.out.println("\"" + code + "\""); From 2ea3fa14c6cb2907a12f204b6017808b2abe8d49 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Feb 2025 13:27:27 +0100 Subject: [PATCH 044/212] First negative test --- .../lib/template_framework/Renderer.java | 2 +- .../tests/TestTemplate.java | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 5c4beb8f89d0b..01eaca2588993 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -76,7 +76,7 @@ static String render(TemplateWithArgs templateWithArgs) { static String render(TemplateWithArgs templateWithArgs, float fuel) { // Check nobody else is using the Renderer. if (renderer != null) { - throw new RendererException("Nested render not allowed."); + throw new RendererException("Nested render not allowed. Please only use 'withArgs' inside Templates, and call 'render' only once at the end."); } renderer = new Renderer(fuel); diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index d6f421282bafc..ade8edb147b74 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -57,6 +57,10 @@ public class TestTemplate { private static final Random RANDOM = Utils.getRandomInstance(); + interface FailingTest { + void run(); + } + public static void main(String[] args) { testSingleLine(); testMultiLine(); @@ -78,6 +82,8 @@ public static void main(String[] args) { testNames2(); testNames3(); testListArgument(); + + checkFails(() -> testFailingNestedRendering(), "Nested render not allowed."); } public static void testSingleLine() { @@ -1032,6 +1038,35 @@ class Z_1 { checkEQ(code, expected); } + public static void testFailingNestedRendering() { + var template1 = Template.make(() -> body( + "alpha\n" + )); + + var template2 = Template.make(() -> body( + "beta\n", + // Nested "render" call not allowed! + template1.withArgs().render(), + "gamma\n" + )); + + String code = template2.withArgs().render(); + } + + public static void checkFails(FailingTest test, String errorPrefix) { + try { + test.run(); + System.out.println("checkFails should have thrown with prefix: " + errorPrefix); + throw new RuntimeException("Should have thrown!"); + } catch(RendererException e) { + if (!e.getMessage().startsWith(errorPrefix)) { + System.out.println("checkFails should have thrown with prefix: " + errorPrefix); + System.out.println("got: " + e.getMessage()); + throw new RuntimeException("checkFails prefix mismatch", e); + } + } + } + public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { System.out.println("\"" + code + "\""); From b85a0af876b659542f552d40ac0ddb51f10f0601 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Feb 2025 14:36:41 +0100 Subject: [PATCH 045/212] more negative tests --- .../lib/template_framework/Renderer.java | 22 +++++++++---------- .../tests/TestTemplate.java | 9 +++++++- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 01eaca2588993..2d60429bcd605 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -63,8 +63,7 @@ private Renderer(float fuel) { static Renderer getCurrent() { if (renderer == null) { - // TODO update text - which methods are involved? - throw new RendererException("A method such as $ or let was called outside a template rendering. Make sure you are not calling templates yourself, but use use()."); + throw new RendererException("A Template method such as '$' or 'let' was called outside a template rendering."); } return renderer; } @@ -78,16 +77,15 @@ static String render(TemplateWithArgs templateWithArgs, float fuel) { if (renderer != null) { throw new RendererException("Nested render not allowed. Please only use 'withArgs' inside Templates, and call 'render' only once at the end."); } - - renderer = new Renderer(fuel); - renderer.renderTemplateWithArgs(templateWithArgs); - renderer.checkFrameConsistencyAfterRendering(); - String code = renderer.collectCode(); - - // Release the Renderer. - renderer = null; - - return code; + try { + renderer = new Renderer(fuel); + renderer.renderTemplateWithArgs(templateWithArgs); + renderer.checkFrameConsistencyAfterRendering(); + return renderer.collectCode(); + } finally { + // Release the Renderer. + renderer = null; + } } private void checkFrameConsistencyAfterRendering() { diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index ade8edb147b74..2ef57fabb6900 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -41,7 +41,7 @@ import jdk.test.lib.Utils; import compiler.lib.template_framework.*; -import compiler.lib.template_framework.Template; + import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.intoHook; import static compiler.lib.template_framework.Template.$; @@ -84,6 +84,13 @@ public static void main(String[] args) { testListArgument(); checkFails(() -> testFailingNestedRendering(), "Nested render not allowed."); + checkFails(() -> $("name"), "A Template method such as"); + checkFails(() -> let("x","y"), "A Template method such as"); + checkFails(() -> fuel(), "A Template method such as"); + checkFails(() -> setFuelCost(1.0f), "A Template method such as"); + checkFails(() -> defineName("name", "int", MUTABLE), "A Template method such as"); + checkFails(() -> countNames("int", MUTABLE), "A Template method such as"); + checkFails(() -> sampleName("int", MUTABLE), "A Template method such as"); } public static void testSingleLine() { From da5ea0d3f183fe28e39bd98b80d202f9e0a0c404 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Feb 2025 14:49:33 +0100 Subject: [PATCH 046/212] testFailingHook --- .../lib/template_framework/Renderer.java | 10 +++++----- .../template_framework/tests/TestTemplate.java | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 2d60429bcd605..0db658121e70f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -91,11 +91,11 @@ static String render(TemplateWithArgs templateWithArgs, float fuel) { private void checkFrameConsistencyAfterRendering() { // Ensure CodeFrame consistency. if (baseCodeFrame != currentCodeFrame) { - throw new RendererException("Renderer did not end up at base CodeFrame."); + throw new RuntimeException("Internal error: Renderer did not end up at base CodeFrame."); } // Ensure TemplateFrame consistency. if (baseTemplateFrame != currentTemplateFrame) { - throw new RendererException("Renderer did not end up at base TemplateFrame."); + throw new RuntimeException("Internal error: Renderer did not end up at base TemplateFrame."); } } @@ -146,7 +146,7 @@ private void renderTemplateWithArgs(TemplateWithArgs templateWithArgs) { renderTokenList(it.tokens()); if (currentTemplateFrame != templateFrame) { - throw new RendererException("TemplateFrame mismatch!"); + throw new RuntimeException("Internal error: TemplateFrame mismatch!"); } currentTemplateFrame = currentTemplateFrame.parent; } @@ -220,7 +220,7 @@ private void renderTokenList(List tokens) { renderToken(t); } if (codeFrame != currentCodeFrame) { - throw new RendererException("CodeFrame mismatch."); + throw new RuntimeException("Internal error: CodeFrame mismatch."); } } @@ -242,6 +242,6 @@ private CodeFrame codeFrameForHook(Hook hook) { } codeFrame = codeFrame.parent; } - throw new RendererException("hook " + hook.name() + " was referenced but not found!"); + throw new RendererException("Hook '" + hook.name() + "' was referenced but not found!"); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 2ef57fabb6900..b92745cce8075 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -91,6 +91,7 @@ public static void main(String[] args) { checkFails(() -> defineName("name", "int", MUTABLE), "A Template method such as"); checkFails(() -> countNames("int", MUTABLE), "A Template method such as"); checkFails(() -> sampleName("int", MUTABLE), "A Template method such as"); + checkFails(() -> testFailingHook(), "Hook 'Hook1' was referenced but not found!"); } public static void testSingleLine() { @@ -1060,6 +1061,23 @@ public static void testFailingNestedRendering() { String code = template2.withArgs().render(); } + public static void testFailingHook() { + var hook1 = new Hook("Hook1"); + + var template1 = Template.make(() -> body( + "alpha\n" + )); + + var template2 = Template.make(() -> body( + "beta\n", + // Use hook without hook1.set + intoHook(hook1, template1.withArgs()), + "gamma\n" + )); + + String code = template2.withArgs().render(); + } + public static void checkFails(FailingTest test, String errorPrefix) { try { test.run(); From a0544855f6ec5f8c549e8582a9169892439e0d8f Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Feb 2025 14:55:57 +0100 Subject: [PATCH 047/212] testFailingSample --- .../jtreg/compiler/lib/template_framework/NameSet.java | 2 +- .../template_framework/tests/TestTemplate.java | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java b/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java index 04b472d726787..c7f8482fe5758 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java @@ -53,7 +53,7 @@ public int count(Object type) { public String sample(Object type) { int c = count(type); if (c == 0) { - throw new RendererException("No variable of type " + type.toString()); + throw new RendererException("No variable of type '" + type.toString() + "'."); } // Maybe sample from parent. diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index b92745cce8075..fbfa7a7978d59 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -92,6 +92,7 @@ public static void main(String[] args) { checkFails(() -> countNames("int", MUTABLE), "A Template method such as"); checkFails(() -> sampleName("int", MUTABLE), "A Template method such as"); checkFails(() -> testFailingHook(), "Hook 'Hook1' was referenced but not found!"); + checkFails(() -> testFailingSample(), "No variable of type 'int'."); } public static void testSingleLine() { @@ -1078,6 +1079,15 @@ public static void testFailingHook() { String code = template2.withArgs().render(); } + public static void testFailingSample() { + var template1 = Template.make(() -> body( + let("v", sampleName("int", MUTABLE)), + "v is #v\n" + )); + + String code = template1.withArgs().render(); + } + public static void checkFails(FailingTest test, String errorPrefix) { try { test.run(); From a73ee6c1fd637837220b399293236e8b829ffa7c Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Feb 2025 15:04:50 +0100 Subject: [PATCH 048/212] testFailingHashtag --- .../tests/TestTemplate.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index fbfa7a7978d59..8eefd29bd2f5d 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -93,6 +93,10 @@ public static void main(String[] args) { checkFails(() -> sampleName("int", MUTABLE), "A Template method such as"); checkFails(() -> testFailingHook(), "Hook 'Hook1' was referenced but not found!"); checkFails(() -> testFailingSample(), "No variable of type 'int'."); + checkFails(() -> testFailingHashtag1(), "Duplicate hashtag replacement for #a"); + checkFails(() -> testFailingHashtag2(), "Duplicate hashtag replacement for #a"); + checkFails(() -> testFailingHashtag3(), "Duplicate hashtag replacement for #a"); + checkFails(() -> testFailingHashtag4(), "Missing hashtag replacement for #a"); } public static void testSingleLine() { @@ -1088,6 +1092,41 @@ public static void testFailingSample() { String code = template1.withArgs().render(); } + public static void testFailingHashtag1() { + var template1 = Template.make("a", "a", (String _, String _) -> body( + "nothing\n" + )); + + String code = template1.withArgs("x", "y").render(); + } + + public static void testFailingHashtag2() { + var template1 = Template.make("a", (String _) -> body( + let("a", "x"), + "nothing\n" + )); + + String code = template1.withArgs("y").render(); + } + + public static void testFailingHashtag3() { + var template1 = Template.make(() -> body( + let("a", "x"), + let("a", "y"), + "nothing\n" + )); + + String code = template1.withArgs().render(); + } + + public static void testFailingHashtag4() { + var template1 = Template.make(() -> body( + "#a\n" + )); + + String code = template1.withArgs().render(); + } + public static void checkFails(FailingTest test, String errorPrefix) { try { test.run(); From 7f454fc037aac2cfd60a09928d0373fc7c1481c6 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Feb 2025 15:13:05 +0100 Subject: [PATCH 049/212] binding test --- .../lib/template_framework/CodeFrame.java | 2 +- .../tests/TestTemplate.java | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java index 03dd199e56d19..42d831ab5d0a8 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java @@ -75,7 +75,7 @@ void addCode(Code code) { void addHook(Hook hook) { if (hasHook(hook)) { - throw new RendererException("Duplicate Hook in Template: " + hook.name()); + throw new RuntimeException("Internal error: Duplicate Hook in CodeFrame: " + hook.name()); } hookCodeLists.put(hook, new Code.CodeList(new ArrayList())); } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 8eefd29bd2f5d..4124f5ba2632a 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -97,6 +97,8 @@ public static void main(String[] args) { checkFails(() -> testFailingHashtag2(), "Duplicate hashtag replacement for #a"); checkFails(() -> testFailingHashtag3(), "Duplicate hashtag replacement for #a"); checkFails(() -> testFailingHashtag4(), "Missing hashtag replacement for #a"); + checkFails(() -> testFailingBinding1(), "Duplicate 'bind' not allowed."); + checkFails(() -> testFailingBinding2(), "Cannot 'get' before 'bind'."); } public static void testSingleLine() { @@ -1127,6 +1129,23 @@ public static void testFailingHashtag4() { String code = template1.withArgs().render(); } + public static void testFailingBinding1() { + var binding = new TemplateBinding(); + var template1 = Template.make(() -> body( + "nothing\n" + )); + binding.bind(template1); + binding.bind(template1); + } + + public static void testFailingBinding2() { + var binding = new TemplateBinding(); + var template1 = Template.make(() -> body( + "nothing\n", + binding.get().withArgs() + )); + String code = template1.withArgs().render(); + } public static void checkFails(FailingTest test, String errorPrefix) { try { test.run(); From 6e2b48ae9e2e34e8bd7c5c71e1568bc5f45877b2 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Feb 2025 15:41:05 +0100 Subject: [PATCH 050/212] body illegal argument examples --- .../lib/template_framework/CodeFrame.java | 2 +- .../lib/template_framework/Renderer.java | 1 - .../lib/template_framework/Template.java | 3 + .../lib/template_framework/Token.java | 4 +- .../tests/TestTemplate.java | 58 ++++++++++++------- 5 files changed, 45 insertions(+), 23 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java index 42d831ab5d0a8..88d34b70f0d8d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java @@ -75,6 +75,7 @@ void addCode(Code code) { void addHook(Hook hook) { if (hasHook(hook)) { + // This should never happen, as we add a dedicated CodeFrame for each hook. throw new RuntimeException("Internal error: Duplicate Hook in CodeFrame: " + hook.name()); } hookCodeLists.put(hook, new Code.CodeList(new ArrayList())); @@ -104,7 +105,6 @@ String sampleName(Object type, NameSelection nameSelection) { return nameSet(nameSelection).sample(type); } - // TODO ensure only use once! Code getCode() { return new Code.CodeList(codeList); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 0db658121e70f..2cd1a8486cfec 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -45,7 +45,6 @@ public class Renderer { */ private static Renderer renderer = null; - // TODO describe private int nextTemplateFrameId; private TemplateFrame baseTemplateFrame; private TemplateFrame currentTemplateFrame; diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 12a9f81c8e6ff..bff6877d3493f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -73,6 +73,9 @@ public TemplateWithArgs.TwoArgsUse withArgs(A a, B b) { } static TemplateBody body(Object... tokens) { + if (tokens == null) { + throw new IllegalArgumentException("Unexpected tokens: null"); + } return new TemplateBody(Token.parse(Arrays.asList(tokens))); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java index 86d39c00fab68..c0a5c259c3119 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java @@ -48,6 +48,9 @@ private static void parseList(List inputList, List outputList) { } private static void parseToken(Object o, List outputList) { + if (o == null) { + throw new IllegalArgumentException("Unexpected token: null"); + } switch (o) { case Token t -> outputList.add(t); case String s -> outputList.add(new StringToken(s)); @@ -56,7 +59,6 @@ private static void parseToken(Object o, List outputList) { case Double s -> outputList.add(new StringToken(s.toString())); case Float s -> outputList.add(new StringToken(s.toString())); case List l -> parseList(l, outputList); - //case Hook h -> { // TODO some known cases default -> throw new IllegalArgumentException("Unexpected token: " + o); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 4124f5ba2632a..dad2f1260149a 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -83,22 +83,25 @@ public static void main(String[] args) { testNames3(); testListArgument(); - checkFails(() -> testFailingNestedRendering(), "Nested render not allowed."); - checkFails(() -> $("name"), "A Template method such as"); - checkFails(() -> let("x","y"), "A Template method such as"); - checkFails(() -> fuel(), "A Template method such as"); - checkFails(() -> setFuelCost(1.0f), "A Template method such as"); - checkFails(() -> defineName("name", "int", MUTABLE), "A Template method such as"); - checkFails(() -> countNames("int", MUTABLE), "A Template method such as"); - checkFails(() -> sampleName("int", MUTABLE), "A Template method such as"); - checkFails(() -> testFailingHook(), "Hook 'Hook1' was referenced but not found!"); - checkFails(() -> testFailingSample(), "No variable of type 'int'."); - checkFails(() -> testFailingHashtag1(), "Duplicate hashtag replacement for #a"); - checkFails(() -> testFailingHashtag2(), "Duplicate hashtag replacement for #a"); - checkFails(() -> testFailingHashtag3(), "Duplicate hashtag replacement for #a"); - checkFails(() -> testFailingHashtag4(), "Missing hashtag replacement for #a"); - checkFails(() -> testFailingBinding1(), "Duplicate 'bind' not allowed."); - checkFails(() -> testFailingBinding2(), "Cannot 'get' before 'bind'."); + expectRendererException(() -> testFailingNestedRendering(), "Nested render not allowed."); + expectRendererException(() -> $("name"), "A Template method such as"); + expectRendererException(() -> let("x","y"), "A Template method such as"); + expectRendererException(() -> fuel(), "A Template method such as"); + expectRendererException(() -> setFuelCost(1.0f), "A Template method such as"); + expectRendererException(() -> defineName("name", "int", MUTABLE), "A Template method such as"); + expectRendererException(() -> countNames("int", MUTABLE), "A Template method such as"); + expectRendererException(() -> sampleName("int", MUTABLE), "A Template method such as"); + expectRendererException(() -> testFailingHook(), "Hook 'Hook1' was referenced but not found!"); + expectRendererException(() -> testFailingSample(), "No variable of type 'int'."); + expectRendererException(() -> testFailingHashtag1(), "Duplicate hashtag replacement for #a"); + expectRendererException(() -> testFailingHashtag2(), "Duplicate hashtag replacement for #a"); + expectRendererException(() -> testFailingHashtag3(), "Duplicate hashtag replacement for #a"); + expectRendererException(() -> testFailingHashtag4(), "Missing hashtag replacement for #a"); + expectRendererException(() -> testFailingBinding1(), "Duplicate 'bind' not allowed."); + expectRendererException(() -> testFailingBinding2(), "Cannot 'get' before 'bind'."); + expectIllegalArgumentException(() -> body(null), "Unexpected tokens: null"); + expectIllegalArgumentException(() -> body("x", null), "Unexpected token: null"); + expectIllegalArgumentException(() -> body(new Hook("Hook1")), "Unexpected token:"); } public static void testSingleLine() { @@ -1146,16 +1149,31 @@ public static void testFailingBinding2() { )); String code = template1.withArgs().render(); } - public static void checkFails(FailingTest test, String errorPrefix) { + + public static void expectRendererException(FailingTest test, String errorPrefix) { try { test.run(); - System.out.println("checkFails should have thrown with prefix: " + errorPrefix); + System.out.println("Should have thrown with prefix: " + errorPrefix); throw new RuntimeException("Should have thrown!"); } catch(RendererException e) { if (!e.getMessage().startsWith(errorPrefix)) { - System.out.println("checkFails should have thrown with prefix: " + errorPrefix); + System.out.println("Should have thrown with prefix: " + errorPrefix); + System.out.println("got: " + e.getMessage()); + throw new RuntimeException("Prefix mismatch", e); + } + } + } + + public static void expectIllegalArgumentException(FailingTest test, String errorPrefix) { + try { + test.run(); + System.out.println("Should have thrown 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("checkFails prefix mismatch", e); + throw new RuntimeException("Prefix mismatch", e); } } } From 89c177a13c14eb9be19dd062cceb7a464717d059 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Feb 2025 15:51:44 +0100 Subject: [PATCH 051/212] fix token parsing and add tests --- .../jtreg/compiler/lib/template_framework/Hook.java | 5 +---- .../jtreg/compiler/lib/template_framework/Template.java | 6 +----- .../jtreg/compiler/lib/template_framework/Token.java | 8 ++++++-- .../template_framework/tests/TestTemplate.java | 4 ++++ 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java index 3621fe187b270..e8a604c6add17 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java @@ -24,12 +24,9 @@ package compiler.lib.template_framework; import java.util.List; -import java.util.Arrays; public record Hook(String name) { public HookSetToken set(Object... tokens) { - return new HookSetToken(this, Token.parse(Arrays.asList(tokens))); + return new HookSetToken(this, Token.parse(tokens)); } - - // TODO hook.send(...) vs intoHook(hook, ...) } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index bff6877d3493f..a2cf9a6b8aa7e 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -23,7 +23,6 @@ package compiler.lib.template_framework; -import java.util.Arrays; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; @@ -73,10 +72,7 @@ public TemplateWithArgs.TwoArgsUse withArgs(A a, B b) { } static TemplateBody body(Object... tokens) { - if (tokens == null) { - throw new IllegalArgumentException("Unexpected tokens: null"); - } - return new TemplateBody(Token.parse(Arrays.asList(tokens))); + return new TemplateBody(Token.parse(tokens)); } static HookIntoToken intoHook(Hook hook, TemplateWithArgs t) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java index c0a5c259c3119..dfaca18f6cb0d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java @@ -23,6 +23,7 @@ package compiler.lib.template_framework; +import java.util.Arrays; import java.util.ArrayList; import java.util.List; @@ -35,9 +36,12 @@ sealed interface Token permits StringToken, HookIntoToken, NothingToken { - static List parse(Object object) { + static List parse(Object[] objects) { + if (objects == null) { + throw new IllegalArgumentException("Unexpected tokens: null"); + } List outputList = new ArrayList(); - parseToken(object, outputList); + parseToken(Arrays.asList(objects), outputList); return outputList; } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index dad2f1260149a..fe334547eb299 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -102,6 +102,10 @@ public static void main(String[] args) { expectIllegalArgumentException(() -> body(null), "Unexpected tokens: null"); expectIllegalArgumentException(() -> body("x", null), "Unexpected token: null"); expectIllegalArgumentException(() -> body(new Hook("Hook1")), "Unexpected token:"); + Hook hook1 = new Hook("Hook1"); + expectIllegalArgumentException(() -> hook1.set(null), "Unexpected tokens: null"); + expectIllegalArgumentException(() -> hook1.set("x", null), "Unexpected token: null"); + expectIllegalArgumentException(() -> hook1.set(hook1), "Unexpected token:"); } public static void testSingleLine() { From 0c5eb272a403e720734d2235211aa3e0cf68cdbd Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Feb 2025 17:08:34 +0100 Subject: [PATCH 052/212] TestManyTests and Library --- .../lib/template_framework/Library.java | 73 +++++++++++++++ .../examples/TestManyTests.java | 88 +++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/Library.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestManyTests.java diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java new file mode 100644 index 0000000000000..9161c5562fb7d --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -0,0 +1,73 @@ +/* + * 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; + +import java.util.List; + +import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.let; + +/** + * The Library provides a collection of helpful Templates and Hooks. + */ +public abstract class Library { + public static final Hook CLASS_HOOK = new Hook("Class"); + public static final Hook METHOD_HOOK = new Hook("Method"); + + public record TestClassInfo(String classpath, String packageName, String className, List imports) {}; + + public static final Template.TwoArgs> TEST_CLASS = + Template.make("info", "templates", (TestClassInfo info, List templates) -> body( + let("classpath", info.classpath), + let("packageName", info.packageName), + let("className", info.className), + """ + package #packageName; + + import compiler.lib.ir_framework.*; + + public class #className { + + // --- CLASS_HOOK insertions start --- + """, + CLASS_HOOK.set( + """ + // --- CLASS_HOOK insertions end --- + + public static void main() { + TestFramework framework = new TestFramework(#className.class); + framework.addFlags("-classpath", "#classpath"); + framework.start(); + } + + // --- LIST OF TEMPLATES start --- + """, + templates + ), + """ + // --- LIST OF TEMPLATES end --- + } + """ + )); +} diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestManyTests.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestManyTests.java new file mode 100644 index 0000000000000..ef72d5e0783f4 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestManyTests.java @@ -0,0 +1,88 @@ +/* + * 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 + * @summary Test templates which creates many tests and runs them with the TestFramework. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @compile ../../../compiler/lib/ir_framework/TestFramework.java + * @run driver template_framework.examples.TestManyTests + */ + +package template_framework.examples; + +import java.util.List; + +import compiler.lib.compile_framework.*; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateWithArgs; +import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.let; +import static compiler.lib.template_framework.Library.CLASS_HOOK; +import static compiler.lib.template_framework.Library.METHOD_HOOK; +import static compiler.lib.template_framework.Library.TEST_CLASS; +import compiler.lib.template_framework.Library.TestClassInfo; + +public class TestManyTests { + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("p.xyz.InnerTest", generate(comp)); + + // Compile the source file. + comp.compile(); + + // Object ret = p.xyz.InnterTest.main(); + Object ret = comp.invoke("p.xyz.InnerTest", "main", new Object[] {}); + } + + // Generate a source Java file as String + public static String generate(CompileFramework comp) { + // Create the info required for the test class. + TestClassInfo info = new TestClassInfo(comp.getEscapedClassPathOfCompiledClasses(), + "p.xyz", "InnerTest", List.of()); + + var template1 = Template.make(() -> body( + """ + // --- $test start --- + + @Test + public static Object $test() { return null; } + + // --- $test end --- + """ + )); + + List templates = List.of( + template1.withArgs(), + template1.withArgs() + ); + + // Create the test class, which runs all templates. + return TEST_CLASS.withArgs(info, templates).render(); + } +} From 0e583683841513ec8daa2cb6ade13259ffa3b082 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Feb 2025 17:42:55 +0100 Subject: [PATCH 053/212] finish TestManyTests --- .../lib/template_framework/Library.java | 11 ++- .../examples/TestManyTests.java | 85 ++++++++++++++++--- 2 files changed, 81 insertions(+), 15 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index 9161c5562fb7d..0fc53dde6022c 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -35,17 +35,22 @@ public abstract class Library { public static final Hook CLASS_HOOK = new Hook("Class"); public static final Hook METHOD_HOOK = new Hook("Method"); - public record TestClassInfo(String classpath, String packageName, String className, List imports) {}; + public record IRTestClassInfo(String classpath, String packageName, String className, List imports) {}; - public static final Template.TwoArgs> TEST_CLASS = - Template.make("info", "templates", (TestClassInfo info, List templates) -> body( + public static final Template.TwoArgs> IR_TEST_CLASS = + Template.make("info", "templates", (IRTestClassInfo info, List templates) -> body( let("classpath", info.classpath), let("packageName", info.packageName), let("className", info.className), """ package #packageName; + // --- IMPORTS start --- import compiler.lib.ir_framework.*; + """, + info.imports.stream().map(i -> "import " + i + ";\n").toList(), + """ + // --- IMPORTS end --- public class #className { diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestManyTests.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestManyTests.java index ef72d5e0783f4..28405a5547292 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestManyTests.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestManyTests.java @@ -23,10 +23,12 @@ /* * @test - * @summary Test templates which creates many tests and runs them with the TestFramework. + * @summary Test templates which creates many tests and runs them with the IR TestFramework. * @modules java.base/jdk.internal.misc * @library /test/lib / * @compile ../../../compiler/lib/ir_framework/TestFramework.java + * @compile ../../../compiler/lib/generators/Generators.java + * @compile ../../../compiler/lib/verify/Verify.java * @run driver template_framework.examples.TestManyTests */ @@ -35,15 +37,26 @@ import java.util.List; import compiler.lib.compile_framework.*; +import compiler.lib.generators.*; import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateWithArgs; import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Library.CLASS_HOOK; import static compiler.lib.template_framework.Library.METHOD_HOOK; -import static compiler.lib.template_framework.Library.TEST_CLASS; -import compiler.lib.template_framework.Library.TestClassInfo; +import static compiler.lib.template_framework.Library.IR_TEST_CLASS; +import compiler.lib.template_framework.Library.IRTestClassInfo; +/** + * This is a basic IR verification test, in combination with Generators for random input generation + * and Verify for output verification. + *

+ * The "@compile" command for JTREG is required so that the frameworks used in the Template code + * are compiled and available for the Test-VM. + *

+ * Additionally, we must set the classpath for the Test-VM, so that it has access to all compiled + * classes (see {@link CompileFramework#getEscapedClassPathOfCompiledClasses}). + */ public class TestManyTests { public static void main(String[] args) { @@ -63,26 +76,74 @@ public static void main(String[] args) { // Generate a source Java file as String public static String generate(CompileFramework comp) { // Create the info required for the test class. - TestClassInfo info = new TestClassInfo(comp.getEscapedClassPathOfCompiledClasses(), - "p.xyz", "InnerTest", List.of()); + // It is imporant that we pass the classpath to the Test-VM, so that it has access + // to all compiled classes. + IRTestClassInfo info = new IRTestClassInfo(comp.getEscapedClassPathOfCompiledClasses(), + "p.xyz", "InnerTest", + List.of("compiler.lib.generators.*", + "compiler.lib.verify.*")); - var template1 = Template.make(() -> body( + // We define a Test-Template: + // - static fields for inputs: INPUT_A and INPUT_B + // - Data generated with Generators and hashtag replacement #con1. + // - GOLD value precomputed with dedicated call to test. + // - This ensures that the GOLD value is computed in the interpreter + // most likely, since the test method is not yet compiled. + // This allows us later to compare to the results of the compiled + // code. + // The input data is cloned, so that the original INPUT_A is never + // modified and can serve as identical input in later calls to test. + // - In the Setup method, we clone the input data, since the input data + // could be modified inside the test method. + // - The test method makes use of hashtag replacements (#con2 and #op). + // - The Check method verifies the results of the test method with the + // GOLD value. + var template1 = Template.make("op", (String op) -> body( + let("size", Generators.G.safeRestrict(Generators.G.ints(), 10_000, 20_000).next()), + let("con1", Generators.G.ints().next()), + let("con2", Generators.G.safeRestrict(Generators.G.ints(), 1, Integer.MAX_VALUE).next()), """ // --- $test start --- + // $test with size=#size and op=#op + private static int[] $INPUT_A = new int[#size]; + static { + Generators.G.fill(Generators.G.ints(), $INPUT_A); + } + + private static int $INPUT_B = #con1; + private static Object $GOLD = $test($INPUT_A.clone(), $INPUT_B); + + @Setup + public static Object[] $setup() { + // Must make sure to clone input arrays, if it is mutated in the test. + return new Object[] {$INPUT_A.clone(), $INPUT_B}; + } + @Test - public static Object $test() { return null; } + @Arguments(setup = "$setup") + public static Object $test(int[] a, int b) { + for (int i = 0; i < a.length; i++) { + int con = #con2; + a[i] = (a[i] * con) #op b; + } + return a; + } + + @Check(test = "$test") + public static void $check(Object result) { + Verify.checkEQ(result, $GOLD); + } // --- $test end --- """ )); - List templates = List.of( - template1.withArgs(), - template1.withArgs() - ); + // From a list of operators, create a list of templates with applied arguments. + List ops = List.of("+", "-", "*", "&", "|"); + List templates = ops.stream().map(op -> (TemplateWithArgs)template1.withArgs(op)).toList(); // Create the test class, which runs all templates. - return TEST_CLASS.withArgs(info, templates).render(); + return IR_TEST_CLASS.withArgs(info, templates).render(); } } From 19e61c35a94db9d928904571b513dafc95d4f2fa Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Feb 2025 17:47:36 +0100 Subject: [PATCH 054/212] rename test --- .../examples/{TestManyTests.java => TestIRTestClass.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename test/hotspot/jtreg/testlibrary_tests/template_framework/examples/{TestManyTests.java => TestIRTestClass.java} (98%) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestManyTests.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestIRTestClass.java similarity index 98% rename from test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestManyTests.java rename to test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestIRTestClass.java index 28405a5547292..eceda50051b41 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestManyTests.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestIRTestClass.java @@ -29,7 +29,7 @@ * @compile ../../../compiler/lib/ir_framework/TestFramework.java * @compile ../../../compiler/lib/generators/Generators.java * @compile ../../../compiler/lib/verify/Verify.java - * @run driver template_framework.examples.TestManyTests + * @run driver template_framework.examples.TestIRTestClass */ package template_framework.examples; @@ -57,7 +57,7 @@ * Additionally, we must set the classpath for the Test-VM, so that it has access to all compiled * classes (see {@link CompileFramework#getEscapedClassPathOfCompiledClasses}). */ -public class TestManyTests { +public class TestIRTestClass { public static void main(String[] args) { // Create a new CompileFramework instance. From 31c969cc5d0a37708601018b8292014c51c2d9ce Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 11 Feb 2025 10:08:12 +0100 Subject: [PATCH 055/212] stub of EXPRESSIONS, stub of TestLibrary.java --- .../lib/template_framework/Library.java | 50 ++++++- .../examples/TestFuzzExpression.java | 129 ++++++++++++++++++ .../template_framework/tests/TestLibrary.java | 71 ++++++++++ 3 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestLibrary.java diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index 0fc53dde6022c..520000f07e8d5 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -23,8 +23,11 @@ package compiler.lib.template_framework; +import java.util.Arrays; import java.util.List; +import compiler.lib.generators.*; + import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.let; @@ -32,6 +35,11 @@ * The Library provides a collection of helpful Templates and Hooks. */ public abstract class Library { + private static final RestrictableGenerator GEN_INT = Generators.G.ints(); + private static final RestrictableGenerator GEN_LONG = Generators.G.longs(); + private static final Generator GEN_FLOAT = Generators.G.floats(); + private static final Generator GEN_DOUBLE = Generators.G.doubles(); + public static final Hook CLASS_HOOK = new Hook("Class"); public static final Hook METHOD_HOOK = new Hook("Method"); @@ -74,5 +82,45 @@ public static void main() { // --- LIST OF TEMPLATES end --- } """ - )); + ) + ); + + public static String format(int v) { return String.valueOf(v); } + public static String format(long v) { return String.valueOf(v) + "L"; } + + public static String format(float v) { return String.valueOf(v) + "f"; } + public static String format(double v) { return String.valueOf(v); } + + public enum ExpressionType { + INT("int"), + LONG("long"), + FLOAT("float"), + DOUBLE("double"); + + private final String text; + + ExpressionType(final String text) { this.text = text; } + + @Override + public String toString() { return text; } + }; + + public static final List ALL_EXPRESSION_TYPES = Arrays.asList(ExpressionType.class.getEnumConstants()); + + public static final Template.OneArgs CONSTANT = + Template.make("type", (ExpressionType type) -> body( + switch (type) { + case ExpressionType.INT -> format(GEN_INT.next()); + case ExpressionType.LONG -> format(GEN_LONG.next()); + case ExpressionType.FLOAT -> format(GEN_FLOAT.next()); + case ExpressionType.DOUBLE -> format(GEN_DOUBLE.next()); + } + ) + ); + + public static final Template.OneArgs EXPRESSION = + Template.make("type", (ExpressionType type) -> body( + CONSTANT.withArgs(type) + ) + ); } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java new file mode 100644 index 0000000000000..d1612c197a113 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java @@ -0,0 +1,129 @@ +/* + * 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 + * @summary Test with example expressions that are fuzzed. The example expressions + * have inputs, which we can fill with random expressions. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @compile ../../../compiler/lib/ir_framework/TestFramework.java + * @compile ../../../compiler/lib/generators/Generators.java + * @compile ../../../compiler/lib/verify/Verify.java + * @run driver template_framework.examples.TestFuzzExpression + */ + +package template_framework.examples; + +import java.util.List; + +import compiler.lib.compile_framework.*; +import compiler.lib.generators.*; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateWithArgs; +import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.let; +import static compiler.lib.template_framework.Library.CLASS_HOOK; +import static compiler.lib.template_framework.Library.METHOD_HOOK; +import compiler.lib.template_framework.Library.IRTestClassInfo; +import static compiler.lib.template_framework.Library.IR_TEST_CLASS; +import compiler.lib.template_framework.Library.ExpressionType; +import static compiler.lib.template_framework.Library.ALL_EXPRESSION_TYPES; +import static compiler.lib.template_framework.Library.EXPRESSION; + +/** + * This is a basic IR verification test, in combination with Generators for random input generation + * and Verify for output verification. + *

+ * The "@compile" command for JTREG is required so that the frameworks used in the Template code + * are compiled and available for the Test-VM. + *

+ * Additionally, we must set the classpath for the Test-VM, so that it has access to all compiled + * classes (see {@link CompileFramework#getEscapedClassPathOfCompiledClasses}). + */ +public class TestFuzzExpression { + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("p.xyz.InnerTest", generate(comp)); + + // Compile the source file. + comp.compile(); + + // Object ret = p.xyz.InnterTest.main(); + Object ret = comp.invoke("p.xyz.InnerTest", "main", new Object[] {}); + } + + // Generate a source Java file as String + public static String generate(CompileFramework comp) { + // Create the info required for the test class. + // It is imporant that we pass the classpath to the Test-VM, so that it has access + // to all compiled classes. + IRTestClassInfo info = new IRTestClassInfo(comp.getEscapedClassPathOfCompiledClasses(), + "p.xyz", "InnerTest", + List.of("compiler.lib.generators.*", + "compiler.lib.verify.*")); + + var template1 = Template.make("type", (ExpressionType type)-> body( + """ + // --- $test start --- + // type: #type + """, + // We set a dedicated class hook here, so that fields are + // NOT available across the tests. + CLASS_HOOK.set( + """ + + private static Object $GOLD = $test(); + + @Test + public static Object $test() { + """, + METHOD_HOOK.set( + "return ", EXPRESSION.withArgs(type), ";\n" + ), + """ + } + + @Check(test = "$test") + public static void $check(Object result) { + Verify.checkEQ(result, $GOLD); + } + + """ + ), + """ + // --- $test end --- + """ + )); + + // Use template1 with every type. + List templates = ALL_EXPRESSION_TYPES.stream().map(type -> (TemplateWithArgs)template1.withArgs(type)).toList(); + + // Create the test class, which runs all templates. + return IR_TEST_CLASS.withArgs(info, templates).render(); + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestLibrary.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestLibrary.java new file mode 100644 index 0000000000000..16e5102dd998c --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestLibrary.java @@ -0,0 +1,71 @@ +/* + * 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 + * @summary Test some basic Template instantiations. We do not necessarily generate correct + * java code, we just test that the code generation deterministically creates the + * expected String. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver template_framework.tests.TestLibrary + */ + +package template_framework.tests; + +import compiler.lib.compile_framework.*; +import compiler.lib.generators.*; +import compiler.lib.verify.*; +import compiler.lib.template_framework.Template; +import static compiler.lib.template_framework.Template.body; + +import static compiler.lib.template_framework.Library.format; + +public class TestLibrary { + public static void main(String[] args) { + testFormatInt(); + } + + private static void testFormatInt() { + int gold = 1; + CompileFramework comp = new CompileFramework(); + comp.addJavaSourceCode("p.xyz.InnerTest", generate("int", format(gold))); + comp.compile(); + Object ret = comp.invoke("p.xyz.InnerTest", "get", new Object[] {}); + int result = (int)ret; + Verify.checkEQ(result, gold); + } + + private static String generate(String type, String value) { + var template1 = Template.make("type", "value", (String t, String v) -> body( + """ + package p.xyz; + public class InnerTest { + public static #type get() { return #value; } + } + """ + )); + + return template1.withArgs(type, value).render(); + } +} From bcc553e2d36f55e570661fc1bc5d295f58ef855e Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 11 Feb 2025 10:26:18 +0100 Subject: [PATCH 056/212] basic format tests --- .../template_framework/tests/TestLibrary.java | 68 ++++++++++++++++--- 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestLibrary.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestLibrary.java index 16e5102dd998c..0700a6de4658d 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestLibrary.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestLibrary.java @@ -33,39 +33,85 @@ package template_framework.tests; +import java.util.List; +import java.util.ArrayList; + import compiler.lib.compile_framework.*; import compiler.lib.generators.*; import compiler.lib.verify.*; import compiler.lib.template_framework.Template; import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Library.format; public class TestLibrary { + private static final RestrictableGenerator GEN_INT = Generators.G.ints(); + private static final RestrictableGenerator GEN_LONG = Generators.G.longs(); + private static final Generator GEN_FLOAT = Generators.G.floats(); + private static final Generator GEN_DOUBLE = Generators.G.doubles(); + public static void main(String[] args) { - testFormatInt(); + testFormat(); } - private static void testFormatInt() { - int gold = 1; + record FormatInfo(int id, String type, Object value, String formatted) {} + + private static void testFormat() { + List list = new ArrayList(); + + for (int i = 0; i < 1000; i++) { + int v = GEN_INT.next(); + list.add(new FormatInfo(i, "int", v, format(v))); + } + + for (int i = 1000; i < 2000; i++) { + long v = GEN_LONG.next(); + list.add(new FormatInfo(i, "long", v, format(v))); + } + + for (int i = 2000; i < 3000; i++) { + float v = GEN_FLOAT.next(); + list.add(new FormatInfo(i, "float", v, format(v))); + } + + for (int i = 3000; i < 4000; i++) { + double v = GEN_DOUBLE.next(); + list.add(new FormatInfo(i, "double", v, format(v))); + } + CompileFramework comp = new CompileFramework(); - comp.addJavaSourceCode("p.xyz.InnerTest", generate("int", format(gold))); + comp.addJavaSourceCode("p.xyz.InnerTest", generate(list)); comp.compile(); - Object ret = comp.invoke("p.xyz.InnerTest", "get", new Object[] {}); - int result = (int)ret; - Verify.checkEQ(result, gold); + + for (FormatInfo info : list) { + Object ret = comp.invoke("p.xyz.InnerTest", "get_" + info.id, new Object[] {}); + System.out.println("id: " + info.id + " -> " + info.value + " == " + ret); + Verify.checkEQ(ret, info.value); + } } - private static String generate(String type, String value) { - var template1 = Template.make("type", "value", (String t, String v) -> body( + private static String generate(List list) { + var template1 = Template.make("info", (FormatInfo info) -> body( + let("id", info.id()), + let("type", info.type()), + let("formatted", info.formatted()), + """ + public static #type get_#id() { return #formatted; } + """ + )); + + var template2 = Template.make(() -> body( """ package p.xyz; public class InnerTest { - public static #type get() { return #value; } + """, + list.stream().map(info -> template1.withArgs(info)).toList(), + """ } """ )); - return template1.withArgs(type, value).render(); + return template2.withArgs().render(); } } From 81f3d98387776491923b22c6b49ffc4217332b76 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 11 Feb 2025 11:09:18 +0100 Subject: [PATCH 057/212] TestLibrary --- .../lib/template_framework/Library.java | 16 +++--- .../lib/template_framework/Renderer.java | 49 +++++++++++++++++-- .../lib/template_framework/Template.java | 4 +- .../lib/template_framework/Token.java | 10 ++-- .../template_framework/tests/TestLibrary.java | 45 ++++++++--------- 5 files changed, 81 insertions(+), 43 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index 520000f07e8d5..6cdc0a44b0250 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -85,11 +85,11 @@ public static void main() { ) ); - public static String format(int v) { return String.valueOf(v); } - public static String format(long v) { return String.valueOf(v) + "L"; } + static String format(int v) { return String.valueOf(v); } + static String format(long v) { return String.valueOf(v) + "L"; } - public static String format(float v) { return String.valueOf(v) + "f"; } - public static String format(double v) { return String.valueOf(v); } + static String format(float v) { return String.valueOf(v) + "f"; } + static String format(double v) { return String.valueOf(v); } public enum ExpressionType { INT("int"), @@ -110,10 +110,10 @@ public enum ExpressionType { public static final Template.OneArgs CONSTANT = Template.make("type", (ExpressionType type) -> body( switch (type) { - case ExpressionType.INT -> format(GEN_INT.next()); - case ExpressionType.LONG -> format(GEN_LONG.next()); - case ExpressionType.FLOAT -> format(GEN_FLOAT.next()); - case ExpressionType.DOUBLE -> format(GEN_DOUBLE.next()); + case ExpressionType.INT -> GEN_INT.next(); + case ExpressionType.LONG -> GEN_LONG.next(); + case ExpressionType.FLOAT -> GEN_FLOAT.next(); + case ExpressionType.DOUBLE -> GEN_DOUBLE.next(); } ) ); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 2cd1a8486cfec..7b4a4011ff4f6 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -108,8 +108,8 @@ private String collectCode() { return currentTemplateFrame.$(name); } - void addHashtagReplacement(String key, String value) { - currentTemplateFrame.addHashtagReplacement(key, value); + void addHashtagReplacement(String key, Object value) { + currentTemplateFrame.addHashtagReplacement(key, format(value)); } private String getHashtagReplacement(String key) { @@ -136,11 +136,54 @@ String sampleName(Object type, NameSelection nameSelection) { return currentCodeFrame.sampleName(type, nameSelection); } + static String format(Object value) { + return switch (value) { + case String s -> s; + case Integer i -> i.toString(); + case Long l -> l.toString() + "L"; + case Float f -> formatFloat(f); + case Double d -> formatDouble(d); + default -> value.toString(); + }; + } + + private static String formatFloat(Float f) { + if (Float.isFinite(f)) { + return f.toString() + "f"; + } else if (f.isNaN()) { + return "Float.intBitsToFloat(" + Float.floatToRawIntBits(f) + " /* NaN */)"; + } else if (f.isInfinite()) { + if (f > 0) { + return "Float.POSITIVE_INFINITY"; + } else { + return "Float.NEGATIVE_INFINITY"; + } + } else { + throw new RuntimeException("Not handled: " + f); + } + } + + private static String formatDouble(Double d) { + if (Double.isFinite(d)) { + return d.toString(); + } else if (d.isNaN()) { + return "Double.longBitsToDouble(" + Double.doubleToRawLongBits(d) + "L /* NaN */)"; + } else if (d.isInfinite()) { + if (d > 0) { + return "Double.POSITIVE_INFINITY"; + } else { + return "Double.NEGATIVE_INFINITY"; + } + } else { + throw new RuntimeException("Not handled: " + d); + } + } + private void renderTemplateWithArgs(TemplateWithArgs templateWithArgs) { TemplateFrame templateFrame = TemplateFrame.make(currentTemplateFrame, nextTemplateFrameId++); currentTemplateFrame = templateFrame; - templateWithArgs.visitArguments((name, value) -> addHashtagReplacement(name, value.toString())); + templateWithArgs.visitArguments((name, value) -> addHashtagReplacement(name, format(value))); TemplateBody it = templateWithArgs.instantiate(); renderTokenList(it.tokens()); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index a2cf9a6b8aa7e..7173641740157 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -84,12 +84,12 @@ static HookIntoToken intoHook(Hook hook, TemplateWithArgs t) { } static NothingToken let(String key, Object value) { - Renderer.getCurrent().addHashtagReplacement(key, value.toString()); + Renderer.getCurrent().addHashtagReplacement(key, value); return new NothingToken(); } static TemplateBody let(String key, T value, Function function) { - Renderer.getCurrent().addHashtagReplacement(key, value.toString()); + Renderer.getCurrent().addHashtagReplacement(key, value); return function.apply(value); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java index dfaca18f6cb0d..f495ef565df38 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java @@ -57,11 +57,11 @@ private static void parseToken(Object o, List outputList) { } switch (o) { case Token t -> outputList.add(t); - case String s -> outputList.add(new StringToken(s)); - case Integer s -> outputList.add(new StringToken(s.toString())); - case Long s -> outputList.add(new StringToken(s.toString())); - case Double s -> outputList.add(new StringToken(s.toString())); - case Float s -> outputList.add(new StringToken(s.toString())); + case String s -> outputList.add(new StringToken(Renderer.format(s))); + case Integer s -> outputList.add(new StringToken(Renderer.format(s))); + case Long s -> outputList.add(new StringToken(Renderer.format(s))); + case Double s -> outputList.add(new StringToken(Renderer.format(s))); + case Float s -> outputList.add(new StringToken(Renderer.format(s))); case List l -> parseList(l, outputList); default -> throw new IllegalArgumentException("Unexpected token: " + o); } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestLibrary.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestLibrary.java index 0700a6de4658d..d781197122b84 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestLibrary.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestLibrary.java @@ -23,9 +23,7 @@ /* * @test - * @summary Test some basic Template instantiations. We do not necessarily generate correct - * java code, we just test that the code generation deterministically creates the - * expected String. + * @summary Test formatting of Integer, Long, Float and Double. * @modules java.base/jdk.internal.misc * @library /test/lib / * @run driver template_framework.tests.TestLibrary @@ -43,51 +41,47 @@ import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.let; -import static compiler.lib.template_framework.Library.format; - public class TestLibrary { - private static final RestrictableGenerator GEN_INT = Generators.G.ints(); - private static final RestrictableGenerator GEN_LONG = Generators.G.longs(); - private static final Generator GEN_FLOAT = Generators.G.floats(); - private static final Generator GEN_DOUBLE = Generators.G.doubles(); - public static void main(String[] args) { testFormat(); } - record FormatInfo(int id, String type, Object value, String formatted) {} + record FormatInfo(int id, String type, Object value) {} private static void testFormat() { List list = new ArrayList(); for (int i = 0; i < 1000; i++) { - int v = GEN_INT.next(); - list.add(new FormatInfo(i, "int", v, format(v))); + int v = Generators.G.ints().next(); + list.add(new FormatInfo(i, "int", v)); } for (int i = 1000; i < 2000; i++) { - long v = GEN_LONG.next(); - list.add(new FormatInfo(i, "long", v, format(v))); + long v = Generators.G.longs().next(); + list.add(new FormatInfo(i, "long", v)); } for (int i = 2000; i < 3000; i++) { - float v = GEN_FLOAT.next(); - list.add(new FormatInfo(i, "float", v, format(v))); + float v = Generators.G.floats().next(); + list.add(new FormatInfo(i, "float", v)); } for (int i = 3000; i < 4000; i++) { - double v = GEN_DOUBLE.next(); - list.add(new FormatInfo(i, "double", v, format(v))); + double v = Generators.G.doubles().next(); + list.add(new FormatInfo(i, "double", v)); } CompileFramework comp = new CompileFramework(); comp.addJavaSourceCode("p.xyz.InnerTest", generate(list)); comp.compile(); + // Run each of the "get" methods, and check the result. for (FormatInfo info : list) { - Object ret = comp.invoke("p.xyz.InnerTest", "get_" + info.id, new Object[] {}); - System.out.println("id: " + info.id + " -> " + info.value + " == " + ret); - Verify.checkEQ(ret, info.value); + Object ret1 = comp.invoke("p.xyz.InnerTest", "get_let_" + info.id, new Object[] {}); + Object ret2 = comp.invoke("p.xyz.InnerTest", "get_token_" + info.id, new Object[] {}); + System.out.println("id: " + info.id + " -> " + info.value + " == " + ret1 + " == " + ret2); + Verify.checkEQ(ret1, info.value); + Verify.checkEQ(ret2, info.value); } } @@ -95,10 +89,11 @@ private static String generate(List list) { var template1 = Template.make("info", (FormatInfo info) -> body( let("id", info.id()), let("type", info.type()), - let("formatted", info.formatted()), - """ - public static #type get_#id() { return #formatted; } + let("value", info.value()), """ + public static #type get_let_#id() { return #value; } + """, + "public static #type get_token_#id() { return ", info.value(), "; }\n" )); var template2 = Template.make(() -> body( From a1a787ce43aeeeac28594e294dd72d2ec2297a71 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 11 Feb 2025 11:17:14 +0100 Subject: [PATCH 058/212] fix formatting issues, rename test --- .../{TestLibrary.java => TestFormat.java} | 12 ++-- .../tests/TestTemplate.java | 60 +++++++++---------- 2 files changed, 35 insertions(+), 37 deletions(-) rename test/hotspot/jtreg/testlibrary_tests/template_framework/tests/{TestLibrary.java => TestFormat.java} (93%) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestLibrary.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestFormat.java similarity index 93% rename from test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestLibrary.java rename to test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestFormat.java index d781197122b84..24b04223cf740 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestLibrary.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestFormat.java @@ -26,7 +26,7 @@ * @summary Test formatting of Integer, Long, Float and Double. * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run driver template_framework.tests.TestLibrary + * @run driver template_framework.tests.TestFormat */ package template_framework.tests; @@ -41,14 +41,10 @@ import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.let; -public class TestLibrary { - public static void main(String[] args) { - testFormat(); - } - +public class TestFormat { record FormatInfo(int id, String type, Object value) {} - private static void testFormat() { + public static void main(String[] args) { List list = new ArrayList(); for (int i = 0; i < 1000; i++) { @@ -86,6 +82,7 @@ private static void testFormat() { } private static String generate(List list) { + // Generate 2 "get" methods, one that formats via "let" (hashtag), the other via direct token. var template1 = Template.make("info", (FormatInfo info) -> body( let("id", info.id()), let("type", info.type()), @@ -96,6 +93,7 @@ private static String generate(List list) { "public static #type get_token_#id() { return ", info.value(), "; }\n" )); + // For each FormatInfo in list, generate the "get" methods inside InnerTest class. var template2 = Template.make(() -> body( """ package p.xyz; diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index fe334547eb299..a7f049c1c23ab 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -143,7 +143,7 @@ public static void testBodyTokens() { " end" )); String code = template.withArgs().render(); - checkEQ(code, "start 123.45.6 1 and 2 end"); + checkEQ(code, "start 12L3.45.6f 1 and 2 end"); } public static void testWithOneArguments() { @@ -736,18 +736,18 @@ public static void testFuel() { String expected = """ { - [ 3 90.0 - <80.0> - [ 2 80.0 - <70.0> - [ 1 70.0 - <60.0> - [ 0 60.0 - <50.0> - done] 0 60.0 - ] 1 70.0 - ] 2 80.0 - ] 3 90.0 + [ 3 90.0f + <80.0f> + [ 2 80.0f + <70.0f> + [ 1 70.0f + <60.0f> + [ 0 60.0f + <50.0f> + done] 0 60.0f + ] 1 70.0f + ] 2 80.0f + ] 3 90.0f } """; checkEQ(code, expected); @@ -786,24 +786,24 @@ public static void testFuelCustom() { String code = template3.withArgs().render(20.0f); String expected = """ - { 20.0 - [ 3 15.0 - <12.0> - [ 2 12.0 - <9.0> - [ 1 9.0 - <6.0> - [ 0 6.0 - <3.0> - [ -1 3.0 - <0.0> + { 20.0f + [ 3 15.0f + <12.0f> + [ 2 12.0f + <9.0f> + [ 1 9.0f + <6.0f> + [ 0 6.0f + <3.0f> + [ -1 3.0f + <0.0f> done - ] -1 3.0 - ] 0 6.0 - ] 1 9.0 - ] 2 12.0 - ] 3 15.0 - } 20.0 + ] -1 3.0f + ] 0 6.0f + ] 1 9.0f + ] 2 12.0f + ] 3 15.0f + } 20.0f """; checkEQ(code, expected); } From a356ea8d3c2d5bd9fe4c0298853d3781afe49108 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 11 Feb 2025 11:51:36 +0100 Subject: [PATCH 059/212] Library more expression --- .../lib/template_framework/Library.java | 37 +++++++++++++++---- .../examples/TestFuzzExpression.java | 6 ++- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index 6cdc0a44b0250..5969364c4c68b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -30,6 +30,8 @@ import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.let; +import static compiler.lib.template_framework.Template.fuel; +import static compiler.lib.template_framework.Template.setFuelCost; /** * The Library provides a collection of helpful Templates and Hooks. @@ -85,12 +87,6 @@ public static void main() { ) ); - static String format(int v) { return String.valueOf(v); } - static String format(long v) { return String.valueOf(v) + "L"; } - - static String format(float v) { return String.valueOf(v) + "f"; } - static String format(double v) { return String.valueOf(v); } - public enum ExpressionType { INT("int"), LONG("long"), @@ -107,7 +103,7 @@ public enum ExpressionType { public static final List ALL_EXPRESSION_TYPES = Arrays.asList(ExpressionType.class.getEnumConstants()); - public static final Template.OneArgs CONSTANT = + public static final Template.OneArgs CONSTANT_EXPRESSION = Template.make("type", (ExpressionType type) -> body( switch (type) { case ExpressionType.INT -> GEN_INT.next(); @@ -118,9 +114,34 @@ public enum ExpressionType { ) ); + public static final Template.OneArgs TERMINAL_EXPRESSION = + Template.make("type", (ExpressionType type) -> body( + // TODO load + CONSTANT_EXPRESSION.withArgs(type) + ) + ); + + // Use Binding to break recursive cycles. + private static final TemplateBinding> OPERATOR_EXPRESSION_BINDING = new TemplateBinding>(); + public static final Template.OneArgs EXPRESSION = Template.make("type", (ExpressionType type) -> body( - CONSTANT.withArgs(type) + setFuelCost(0), + // TODO not alway operator + (fuel() <= 0) ? TERMINAL_EXPRESSION.withArgs(type) + : OPERATOR_EXPRESSION_BINDING.get().withArgs(type) + ) + ); + + public static final Template.OneArgs OPERATOR_EXPRESSION = + Template.make("type", (ExpressionType type) -> body( + setFuelCost(1.0f), + "(", + EXPRESSION.withArgs(type), + " + ", // TODO operators based on type + EXPRESSION.withArgs(type), + ")" ) ); + static { OPERATOR_EXPRESSION_BINDING.bind(OPERATOR_EXPRESSION); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java index d1612c197a113..6b9219b5d8660 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java @@ -124,6 +124,10 @@ public static String generate(CompileFramework comp) { List templates = ALL_EXPRESSION_TYPES.stream().map(type -> (TemplateWithArgs)template1.withArgs(type)).toList(); // Create the test class, which runs all templates. - return IR_TEST_CLASS.withArgs(info, templates).render(); + // Fuel: + // - 10 for IR_TEST_CLASS + // - 10 for template1 + // - 3 for EXPRESSION: depth of 3 + return IR_TEST_CLASS.withArgs(info, templates).render(23.0f); } } From 4bf25753c9a4f4b4700660f49ccaf7cd8b95ffde Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 11 Feb 2025 14:11:05 +0100 Subject: [PATCH 060/212] binary operators --- .../lib/template_framework/Library.java | 97 +++++++++++++++---- .../tests/TestTemplate.java | 5 - 2 files changed, 78 insertions(+), 24 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index 5969364c4c68b..55d12836bb56a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -25,6 +25,8 @@ import java.util.Arrays; import java.util.List; +import java.util.Random; +import jdk.test.lib.Utils; import compiler.lib.generators.*; @@ -37,6 +39,7 @@ * The Library provides a collection of helpful Templates and Hooks. */ public abstract class Library { + private static final Random RANDOM = Utils.getRandomInstance(); private static final RestrictableGenerator GEN_INT = Generators.G.ints(); private static final RestrictableGenerator GEN_LONG = Generators.G.longs(); private static final Generator GEN_FLOAT = Generators.G.floats(); @@ -84,8 +87,7 @@ public static void main() { // --- LIST OF TEMPLATES end --- } """ - ) - ); + )); public enum ExpressionType { INT("int"), @@ -111,37 +113,94 @@ public enum ExpressionType { case ExpressionType.FLOAT -> GEN_FLOAT.next(); case ExpressionType.DOUBLE -> GEN_DOUBLE.next(); } - ) - ); + )); public static final Template.OneArgs TERMINAL_EXPRESSION = Template.make("type", (ExpressionType type) -> body( // TODO load CONSTANT_EXPRESSION.withArgs(type) - ) - ); + )); // Use Binding to break recursive cycles. - private static final TemplateBinding> OPERATOR_EXPRESSION_BINDING = new TemplateBinding>(); + private static final TemplateBinding> BINARY_OPERATOR_EXPRESSION_BINDING = new TemplateBinding>(); public static final Template.OneArgs EXPRESSION = Template.make("type", (ExpressionType type) -> body( setFuelCost(0), // TODO not alway operator (fuel() <= 0) ? TERMINAL_EXPRESSION.withArgs(type) - : OPERATOR_EXPRESSION_BINDING.get().withArgs(type) - ) + : BINARY_OPERATOR_EXPRESSION_BINDING.get().withArgs(type) + )); + + private record BinaryOperator(String start, ExpressionType t1, String middle, ExpressionType t2, String end) {} + + private static final List INT_BINARY_OPERATORS = List.of( + new BinaryOperator("(", ExpressionType.INT, " + ", ExpressionType.INT, ")"), + new BinaryOperator("(", ExpressionType.INT, " - ", ExpressionType.INT, ")"), + new BinaryOperator("(", ExpressionType.INT, " * ", ExpressionType.INT, ")"), + new BinaryOperator("(", ExpressionType.INT, " / ", ExpressionType.INT, ")"), + new BinaryOperator("(", ExpressionType.INT, " % ", ExpressionType.INT, ")"), + new BinaryOperator("(", ExpressionType.INT, " & ", ExpressionType.INT, ")"), + new BinaryOperator("(", ExpressionType.INT, " | ", ExpressionType.INT, ")"), + new BinaryOperator("(", ExpressionType.INT, " ^ ", ExpressionType.INT, ")"), + new BinaryOperator("(", ExpressionType.INT, " << ", ExpressionType.INT, ")"), + new BinaryOperator("(", ExpressionType.INT, " >> ", ExpressionType.INT, ")"), + new BinaryOperator("(", ExpressionType.INT, " >>> ", ExpressionType.INT, ")") ); - public static final Template.OneArgs OPERATOR_EXPRESSION = - Template.make("type", (ExpressionType type) -> body( - setFuelCost(1.0f), - "(", - EXPRESSION.withArgs(type), - " + ", // TODO operators based on type - EXPRESSION.withArgs(type), - ")" - ) + private static final List LONG_BINARY_OPERATORS = List.of( + new BinaryOperator("(", ExpressionType.LONG, " + ", ExpressionType.LONG, ")"), + new BinaryOperator("(", ExpressionType.LONG, " - ", ExpressionType.LONG, ")"), + new BinaryOperator("(", ExpressionType.LONG, " * ", ExpressionType.LONG, ")"), + new BinaryOperator("(", ExpressionType.LONG, " / ", ExpressionType.LONG, ")"), + new BinaryOperator("(", ExpressionType.LONG, " % ", ExpressionType.LONG, ")"), + new BinaryOperator("(", ExpressionType.LONG, " & ", ExpressionType.LONG, ")"), + new BinaryOperator("(", ExpressionType.LONG, " | ", ExpressionType.LONG, ")"), + new BinaryOperator("(", ExpressionType.LONG, " ^ ", ExpressionType.LONG, ")"), + new BinaryOperator("(", ExpressionType.LONG, " << ", ExpressionType.LONG, ")"), + new BinaryOperator("(", ExpressionType.LONG, " >> ", ExpressionType.LONG, ")"), + new BinaryOperator("(", ExpressionType.LONG, " >>> ", ExpressionType.LONG, ")") ); - static { OPERATOR_EXPRESSION_BINDING.bind(OPERATOR_EXPRESSION); } + + private static final List FLOAT_BINARY_OPERATORS = List.of( + new BinaryOperator("(", ExpressionType.FLOAT, " + ", ExpressionType.FLOAT, ")"), + new BinaryOperator("(", ExpressionType.FLOAT, " - ", ExpressionType.FLOAT, ")"), + new BinaryOperator("(", ExpressionType.FLOAT, " * ", ExpressionType.FLOAT, ")"), + new BinaryOperator("(", ExpressionType.FLOAT, " / ", ExpressionType.FLOAT, ")"), + new BinaryOperator("(", ExpressionType.FLOAT, " % ", ExpressionType.FLOAT, ")") + ); + + private static final List DOUBLE_BINARY_OPERATORS = List.of( + new BinaryOperator("(", ExpressionType.DOUBLE, " + ", ExpressionType.DOUBLE, ")"), + new BinaryOperator("(", ExpressionType.DOUBLE, " - ", ExpressionType.DOUBLE, ")"), + new BinaryOperator("(", ExpressionType.DOUBLE, " * ", ExpressionType.DOUBLE, ")"), + new BinaryOperator("(", ExpressionType.DOUBLE, " / ", ExpressionType.DOUBLE, ")"), + new BinaryOperator("(", ExpressionType.DOUBLE, " % ", ExpressionType.DOUBLE, ")") + ); + + private static List binaryOperators(ExpressionType type) { + return switch (type) { + case ExpressionType.INT -> INT_BINARY_OPERATORS; + case ExpressionType.LONG -> LONG_BINARY_OPERATORS; + case ExpressionType.FLOAT -> FLOAT_BINARY_OPERATORS; + case ExpressionType.DOUBLE -> DOUBLE_BINARY_OPERATORS; + }; + } + + private static T choice(List list) { + if (list.isEmpty()) { return null; } + int i = RANDOM.nextInt(list.size()); + return list.get(i); + } + + public static final Template.OneArgs BINARY_OPERATOR_EXPRESSION = + Template.make("type", (ExpressionType type) -> let("op", choice(binaryOperators(type)), op -> body( + setFuelCost(1.0f), + op.start, + EXPRESSION.withArgs(op.t1), + op.middle, + EXPRESSION.withArgs(op.t2), + op.end + ))); + static { BINARY_OPERATOR_EXPRESSION_BINDING.bind(BINARY_OPERATOR_EXPRESSION); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index a7f049c1c23ab..c0f27898071d4 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -36,9 +36,6 @@ import java.util.Arrays; import java.util.List; import java.util.HashSet; -import java.util.Random; - -import jdk.test.lib.Utils; import compiler.lib.template_framework.*; @@ -55,8 +52,6 @@ import static compiler.lib.template_framework.Template.ALL; public class TestTemplate { - private static final Random RANDOM = Utils.getRandomInstance(); - interface FailingTest { void run(); } From 3e6a24d7b33207edcb070b4300755509e7fb0906 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 11 Feb 2025 14:24:22 +0100 Subject: [PATCH 061/212] run more tests --- .../template_framework/examples/TestFuzzExpression.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java index 6b9219b5d8660..2ccc360328345 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java @@ -36,6 +36,7 @@ package template_framework.examples; import java.util.List; +import java.util.ArrayList; import compiler.lib.compile_framework.*; import compiler.lib.generators.*; @@ -121,7 +122,12 @@ public static String generate(CompileFramework comp) { )); // Use template1 with every type. - List templates = ALL_EXPRESSION_TYPES.stream().map(type -> (TemplateWithArgs)template1.withArgs(type)).toList(); + List templates = new ArrayList<>(); + for (int i = 0; i < 100; i++) { + for (ExpressionType type : ALL_EXPRESSION_TYPES) { + templates.add(template1.withArgs(type)); + } + } // Create the test class, which runs all templates. // Fuel: From e30c5621b4104d434c838b76f7eef962c1a5564b Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 11 Feb 2025 17:07:42 +0100 Subject: [PATCH 062/212] handle exceptions --- .../jtreg/compiler/lib/verify/Verify.java | 26 +++++++++++++++++-- .../examples/TestFuzzExpression.java | 13 ++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index 085ec591b0cc0..c57195d47f122 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -29,7 +29,8 @@ /** * The {@link Verify} class provides a single {@link Verify#checkEQ} static method, which recursively * compares the two {@link Object}s by value. It deconstructs {@link Object[]}, compares boxed primitive - * types, and compares the content of arrays and {@link MemorySegment}s. + * types, compares the content of arrays and {@link MemorySegment}s, and checks that the messages of two + * {@link Exception}s are equal. * * When a comparison fail, then methods print helpful messages, before throwing a {@link VerifyException}. */ @@ -93,11 +94,12 @@ private static void checkEQ(Object a, Object b, String context) { case float[] x -> checkEQimpl(x, (float[])b, context); case double[] x -> checkEQimpl(x, (double[])b, context); case MemorySegment x -> checkEQimpl(x, (MemorySegment) b, context); + case Exception x -> checkEQimpl(x, (Exception) b, context); default -> { System.err.println("ERROR: Verify.checkEQ failed: type not supported: " + ca.getName()); print(a, "a " + context); print(b, "b " + context); - throw new VerifyException("Object array type not supported: " + ca.getName()); + throw new VerifyException("Object type not supported: " + ca.getName()); } } } @@ -202,6 +204,26 @@ private static void checkEQimpl(MemorySegment a, MemorySegment b, String context throw new VerifyException("MemorySegment value mismatch."); } + /** + * Verify that the content of two MemorySegments is identical. Note: we do not check the + * backing type, only the size and content. + */ + private static void checkEQimpl(Exception a, Exception b, String context) { + String am = a.getMessage(); + String bm = b.getMessage(); + + // Missing messages is expected, but if they both have one, they must agree. + if (am == null || bm == null) { return; } + if (am.equals(bm)) { return; } + + System.err.println("ERROR: Verify.checkEQ failed for: " + context); + System.out.println("a: " + a.getMessage()); + System.out.println("b: " + b.getMessage()); + System.out.println(a); + System.out.println(b); + throw new VerifyException("Exception message mismatch: " + a + " vs " + b); + } + /** * Verify that the content of two byte arrays is identical. */ diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java index 2ccc360328345..1fdfa71c283dc 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java @@ -104,7 +104,16 @@ public static String generate(CompileFramework comp) { public static Object $test() { """, METHOD_HOOK.set( - "return ", EXPRESSION.withArgs(type), ";\n" + // We need to catch Exceptions like ArithmeticException, so that we do + // not get ExceptionInInitializerError when loading the class and running + // the static code blocks. + "try {\n", + " return ", EXPRESSION.withArgs(type), ";\n", + """ + } catch (Exception e) { + return e; + } + """ ), """ } @@ -121,7 +130,7 @@ public static String generate(CompileFramework comp) { """ )); - // Use template1 with every type. + // Use template1 100 times with every type. List templates = new ArrayList<>(); for (int i = 0; i < 100; i++) { for (ExpressionType type : ALL_EXPRESSION_TYPES) { From 5bcb8aa16162d9ecb8af2ab68aec606a3a0b441a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 11 Feb 2025 17:20:11 +0100 Subject: [PATCH 063/212] add exception test --- .../verify/tests/TestVerify.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java index bdfeeb149eb47..2b5111edc15cc 100644 --- a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java +++ b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java @@ -50,6 +50,7 @@ public static void main(String[] args) { testArrayFloat(); testArrayDouble(); testNativeMemorySegment(); + testException(); // Test recursive data: Object array of values, etc. testRecursive(); @@ -290,6 +291,29 @@ public static void testNativeMemorySegment() { checkNE(a, c); } + public static void testException() { + Exception e1 = new ArithmeticException("abc"); + Exception e2 = new ArithmeticException("abc"); + Exception e3 = new ArithmeticException(); + Exception e4 = new ArithmeticException("xyz"); + Exception e5 = new RuntimeException("abc"); + + Verify.checkEQ(e1, e1); + Verify.checkEQ(e1, e2); + Verify.checkEQ(e3, e3); + Verify.checkEQ(e1, e3); // one has no message + + checkNE(e1, e4); + checkNE(e2, e4); + Verify.checkEQ(e3, e4); + + Verify.checkEQ(e5, e5); + checkNE(e1, e5); + checkNE(e2, e5); + checkNE(e3, e5); + checkNE(e4, e5); + } + public static void testRecursive() { Verify.checkEQ(null, null); From c504690757e47f26652fe7bce7f9ffd61d040a9b Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 09:46:27 +0100 Subject: [PATCH 064/212] static field load and NaN verify issue --- .../lib/template_framework/Library.java | 49 ++++++++++++++++--- .../jtreg/compiler/lib/verify/Verify.java | 12 +++-- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index 55d12836bb56a..ff20a1eaef8d6 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -32,8 +32,14 @@ import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.let; +import static compiler.lib.template_framework.Template.$; +import static compiler.lib.template_framework.Template.intoHook; import static compiler.lib.template_framework.Template.fuel; import static compiler.lib.template_framework.Template.setFuelCost; +import static compiler.lib.template_framework.Template.countNames; +import static compiler.lib.template_framework.Template.sampleName; +import static compiler.lib.template_framework.Template.ALL; +import static compiler.lib.template_framework.Template.MUTABLE; /** * The Library provides a collection of helpful Templates and Hooks. @@ -45,6 +51,12 @@ public abstract class Library { private static final Generator GEN_FLOAT = Generators.G.floats(); private static final Generator GEN_DOUBLE = Generators.G.doubles(); + private static T choice(List list) { + if (list.isEmpty()) { return null; } + int i = RANDOM.nextInt(list.size()); + return list.get(i); + } + public static final Hook CLASS_HOOK = new Hook("Class"); public static final Hook METHOD_HOOK = new Hook("Method"); @@ -115,10 +127,37 @@ public enum ExpressionType { } )); + public static final Template.TwoArgs DEFINE_STATIC_FIELD = + Template.make("type", "name", (ExpressionType type, String name) -> body( + "public static #type #name = ", CONSTANT_EXPRESSION.withArgs(type), ";\n" + )); + + public static final Template.TwoArgs GENERATE_EARLIER_VALUE = + Template.make("type", "name", (ExpressionType type, String name) -> body( + // TODO alternatives + intoHook(CLASS_HOOK, DEFINE_STATIC_FIELD.withArgs(type, name)) + )); + + public static final Template.OneArgs LOAD_EXPRESSION = + Template.make("type", (ExpressionType type) -> { + if (countNames(type, ALL) == 0 || RANDOM.nextInt(5) == 0) { + return body( + GENERATE_EARLIER_VALUE.withArgs(type, $("early")), + $("early") + ); + } else { + return body( + sampleName(type, ALL) + ); + } + }); + public static final Template.OneArgs TERMINAL_EXPRESSION = Template.make("type", (ExpressionType type) -> body( - // TODO load - CONSTANT_EXPRESSION.withArgs(type) + choice(List.of( + CONSTANT_EXPRESSION.withArgs(type), + LOAD_EXPRESSION.withArgs(type) + )) )); // Use Binding to break recursive cycles. @@ -187,12 +226,6 @@ private static List binaryOperators(ExpressionType type) { }; } - private static T choice(List list) { - if (list.isEmpty()) { return null; } - int i = RANDOM.nextInt(list.size()); - return list.get(i); - } - public static final Template.OneArgs BINARY_OPERATOR_EXPRESSION = Template.make("type", (ExpressionType type) -> let("op", choice(binaryOperators(type)), op -> body( setFuelCost(1.0f), diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index c57195d47f122..5a1f98db919b0 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -155,10 +155,14 @@ private static void checkEQimpl(long a, long b, String context) { } /** - * Verify that two floats have identical bits. + * Ideally, we would want to assert that the Float.floatToRawIntBits are identical. + * But the Java spec allows us to return different bits for a NaN, which allows swapping the inputs + * of an add or mul (NaN1 * NaN2 does not have same bits as NaN2 * NaN1, because the multiplication + * of two NaN values should always return the first of the two). So we verify that we have the same bit + * pattern in all cases, except for NaN we project to the canonical NaN, using Float.floatToIntBits. */ private static void checkEQimpl(float a, float b, String context) { - if (Float.floatToRawIntBits(a) != Float.floatToRawIntBits(b)) { + if (Float.floatToIntBits(a) != Float.floatToIntBits(b)) { System.err.println("ERROR: Verify.checkEQ failed: value mismatch for " + context); System.err.println(" Values: " + a + " vs " + b); System.err.println(" Values: " + Float.floatToRawIntBits(a) + " vs " + Float.floatToRawIntBits(b)); @@ -167,10 +171,10 @@ private static void checkEQimpl(float a, float b, String context) { } /** - * Verify that two doubles have identical bits. + * Same as Float case, see above. */ private static void checkEQimpl(double a, double b, String context) { - if (Double.doubleToRawLongBits(a) != Double.doubleToRawLongBits(b)) { + if (Double.doubleToLongBits(a) != Double.doubleToLongBits(b)) { System.err.println("ERROR: Verify.checkEQ failed: value mismatch for " + context); System.err.println(" Values: " + a + " vs " + b); System.err.println(" Values: " + Double.doubleToRawLongBits(a) + " vs " + Double.doubleToRawLongBits(b)); From 9cef04adeb86f14a8ca39e178c47721187fde788 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 10:25:14 +0100 Subject: [PATCH 065/212] Boolean --- .../compiler/lib/template_framework/Library.java | 14 +++++++++++++- .../compiler/lib/template_framework/Token.java | 1 + test/hotspot/jtreg/compiler/lib/verify/Verify.java | 11 +++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index ff20a1eaef8d6..1b09d027806d7 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -43,6 +43,9 @@ /** * The Library provides a collection of helpful Templates and Hooks. + * + * TODO more operators + * TODO more templates */ public abstract class Library { private static final Random RANDOM = Utils.getRandomInstance(); @@ -105,7 +108,8 @@ public enum ExpressionType { INT("int"), LONG("long"), FLOAT("float"), - DOUBLE("double"); + DOUBLE("double"), + BOOLEAN("boolean"); private final String text; @@ -124,6 +128,7 @@ public enum ExpressionType { case ExpressionType.LONG -> GEN_LONG.next(); case ExpressionType.FLOAT -> GEN_FLOAT.next(); case ExpressionType.DOUBLE -> GEN_DOUBLE.next(); + case ExpressionType.BOOLEAN -> GEN_INT.next() % 2 == 0; // TODO better distribution? } )); @@ -217,12 +222,19 @@ private record BinaryOperator(String start, ExpressionType t1, String middle, Ex new BinaryOperator("(", ExpressionType.DOUBLE, " % ", ExpressionType.DOUBLE, ")") ); + private static final List BOOLEAN_BINARY_OPERATORS = List.of( + new BinaryOperator("(", ExpressionType.BOOLEAN, " || ", ExpressionType.BOOLEAN, ")"), + new BinaryOperator("(", ExpressionType.BOOLEAN, " && ", ExpressionType.BOOLEAN, ")"), + new BinaryOperator("(", ExpressionType.BOOLEAN, " ^ ", ExpressionType.BOOLEAN, ")") + ); + private static List binaryOperators(ExpressionType type) { return switch (type) { case ExpressionType.INT -> INT_BINARY_OPERATORS; case ExpressionType.LONG -> LONG_BINARY_OPERATORS; case ExpressionType.FLOAT -> FLOAT_BINARY_OPERATORS; case ExpressionType.DOUBLE -> DOUBLE_BINARY_OPERATORS; + case ExpressionType.BOOLEAN -> BOOLEAN_BINARY_OPERATORS; }; } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java index f495ef565df38..b7b6a8b4e6645 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java @@ -62,6 +62,7 @@ private static void parseToken(Object o, List outputList) { case Long s -> outputList.add(new StringToken(Renderer.format(s))); case Double s -> outputList.add(new StringToken(Renderer.format(s))); case Float s -> outputList.add(new StringToken(Renderer.format(s))); + case Boolean s -> outputList.add(new StringToken(Renderer.format(s))); case List l -> parseList(l, outputList); default -> throw new IllegalArgumentException("Unexpected token: " + o); } diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index 5a1f98db919b0..a40d6a929c59f 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -86,6 +86,7 @@ private static void checkEQ(Object a, Object b, String context) { case Long x -> checkEQimpl(x, ((Long)b).longValue(), context); case Float x -> checkEQimpl(x, ((Float)b).floatValue(), context); case Double x -> checkEQimpl(x, ((Double)b).doubleValue(), context); + case Boolean x -> checkEQimpl(x, ((Boolean)b).booleanValue(),context); case byte[] x -> checkEQimpl(x, (byte[])b, context); case char[] x -> checkEQimpl(x, (char[])b, context); case short[] x -> checkEQimpl(x, (short[])b, context); @@ -182,6 +183,16 @@ private static void checkEQimpl(double a, double b, String context) { } } + /** + * Verify that two booleans are identical. + */ + private static void checkEQimpl(boolean a, boolean b, String context) { + if (a != b) { + System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + a + " vs " + b + " for " + context); + throw new VerifyException("Value mismatch: " + a + " vs " + b); + } + } + /** * Verify that the content of two MemorySegments is identical. Note: we do not check the * backing type, only the size and content. From 89a11120cde561f39107e3f6c7f31e85cd5f3d46 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 11:01:05 +0100 Subject: [PATCH 066/212] another library template --- .../lib/template_framework/Library.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index 1b09d027806d7..bfbf3ffb42935 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -137,10 +137,27 @@ public enum ExpressionType { "public static #type #name = ", CONSTANT_EXPRESSION.withArgs(type), ";\n" )); + public static final Template.TwoArgs GENERATE_DELAYED_CONSTANT_VIA_BOXING_INLINE = + Template.make("type", "name", (ExpressionType type, String name) -> body( + intoHook(CLASS_HOOK, DEFINE_STATIC_FIELD.withArgs(ExpressionType.BOOLEAN, $("flag"))), + """ + // #name is constant, but only known after Incremental Boxing Inline (after parsing) + Integer $box; + if ($flag) { $box = 1; } else { $box = 2; } + #type #name = ($box == 3) ? """, + CONSTANT_EXPRESSION.withArgs(type), + " : ", + CONSTANT_EXPRESSION.withArgs(type), + ";\n" + )); + public static final Template.TwoArgs GENERATE_EARLIER_VALUE = Template.make("type", "name", (ExpressionType type, String name) -> body( // TODO alternatives - intoHook(CLASS_HOOK, DEFINE_STATIC_FIELD.withArgs(type, name)) + choice(List.of( + intoHook(CLASS_HOOK, DEFINE_STATIC_FIELD.withArgs(type, name)), + intoHook(METHOD_HOOK, GENERATE_DELAYED_CONSTANT_VIA_BOXING_INLINE.withArgs(type, name)) + )) )); public static final Template.OneArgs LOAD_EXPRESSION = From a36f5852487243dfb3e265f4342f506e87af3bb5 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 11:23:50 +0100 Subject: [PATCH 067/212] refactor GENERATE_EARLILER_VALUE_FROM_DELAYED_BOOLEAN --- .../lib/template_framework/Library.java | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index bfbf3ffb42935..fc73961248a21 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -137,14 +137,44 @@ public enum ExpressionType { "public static #type #name = ", CONSTANT_EXPRESSION.withArgs(type), ";\n" )); - public static final Template.TwoArgs GENERATE_DELAYED_CONSTANT_VIA_BOXING_INLINE = - Template.make("type", "name", (ExpressionType type, String name) -> body( + public static final Template.OneArgs GENERATE_DELAYED_BOOLEAN_USING_BOXING_INLINE = + Template.make("name", (String name) -> body( intoHook(CLASS_HOOK, DEFINE_STATIC_FIELD.withArgs(ExpressionType.BOOLEAN, $("flag"))), """ - // #name is constant, but only known after Incremental Boxing Inline (after parsing) + // #name is constant, but only known after Incremental Boxing Inline (after parsing). Integer $box; if ($flag) { $box = 1; } else { $box = 2; } - #type #name = ($box == 3) ? """, + boolean #name = ($box == 3); + """ + )); + + public static final Template.OneArgs GENERATE_DELAYED_BOOLEAN_USING_EMPTY_LOOP = + Template.make("name", (String name) -> body( + """ + // #name is constant, but only known after first loop opts, when loop detected as empty. + int $a = 77; + int $b = 0; + do { + $a--; + $b++; + } while ($a > 0); + boolean #name = ($b == 77); + """ + )); + + public static final Template.OneArgs GENERATE_DELAYED_BOOLEAN = + Template.make("name", (String name) -> body( + choice(List.of( + GENERATE_DELAYED_BOOLEAN_USING_BOXING_INLINE.withArgs(name), + GENERATE_DELAYED_BOOLEAN_USING_EMPTY_LOOP.withArgs(name), + intoHook(CLASS_HOOK, DEFINE_STATIC_FIELD.withArgs(ExpressionType.BOOLEAN, name)) + )) + )); + + public static final Template.TwoArgs GENERATE_EARLILER_VALUE_FROM_DELAYED_BOOLEAN = + Template.make("type", "name", (ExpressionType type, String name) -> body( + GENERATE_DELAYED_BOOLEAN.withArgs($("delayed")), + "#type #name = ($delayed) ? ", CONSTANT_EXPRESSION.withArgs(type), " : ", CONSTANT_EXPRESSION.withArgs(type), @@ -156,7 +186,7 @@ public enum ExpressionType { // TODO alternatives choice(List.of( intoHook(CLASS_HOOK, DEFINE_STATIC_FIELD.withArgs(type, name)), - intoHook(METHOD_HOOK, GENERATE_DELAYED_CONSTANT_VIA_BOXING_INLINE.withArgs(type, name)) + intoHook(METHOD_HOOK, GENERATE_EARLILER_VALUE_FROM_DELAYED_BOOLEAN.withArgs(type, name)) )) )); From e7c9bff384dd6b48b07b8b3f0cfb5be9892a02af Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 11:33:46 +0100 Subject: [PATCH 068/212] rm special fuel handling --- .../jtreg/compiler/lib/template_framework/Library.java | 6 ++---- .../template_framework/examples/TestFuzzExpression.java | 8 +------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index fc73961248a21..b210392971022 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -218,9 +218,8 @@ public enum ExpressionType { public static final Template.OneArgs EXPRESSION = Template.make("type", (ExpressionType type) -> body( setFuelCost(0), - // TODO not alway operator - (fuel() <= 0) ? TERMINAL_EXPRESSION.withArgs(type) - : BINARY_OPERATOR_EXPRESSION_BINDING.get().withArgs(type) + (fuel() <= 0 || RANDOM.nextInt(3) == 0) ? TERMINAL_EXPRESSION.withArgs(type) + : BINARY_OPERATOR_EXPRESSION_BINDING.get().withArgs(type) )); private record BinaryOperator(String start, ExpressionType t1, String middle, ExpressionType t2, String end) {} @@ -287,7 +286,6 @@ private static List binaryOperators(ExpressionType type) { public static final Template.OneArgs BINARY_OPERATOR_EXPRESSION = Template.make("type", (ExpressionType type) -> let("op", choice(binaryOperators(type)), op -> body( - setFuelCost(1.0f), op.start, EXPRESSION.withArgs(op.t1), op.middle, diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java index 1fdfa71c283dc..02dc1972a3af6 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java @@ -137,12 +137,6 @@ public static String generate(CompileFramework comp) { templates.add(template1.withArgs(type)); } } - - // Create the test class, which runs all templates. - // Fuel: - // - 10 for IR_TEST_CLASS - // - 10 for template1 - // - 3 for EXPRESSION: depth of 3 - return IR_TEST_CLASS.withArgs(info, templates).render(23.0f); + return IR_TEST_CLASS.withArgs(info, templates).render(); } } From 1ced8575343ac3132dda692399b5ed25ca0af532 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 11:51:08 +0100 Subject: [PATCH 069/212] operator refactoring --- .../lib/template_framework/Library.java | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index b210392971022..73df088a0b327 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -213,18 +213,33 @@ public enum ExpressionType { )); // Use Binding to break recursive cycles. - private static final TemplateBinding> BINARY_OPERATOR_EXPRESSION_BINDING = new TemplateBinding>(); + private static final TemplateBinding> OPERATOR_EXPRESSION_BINDING = new TemplateBinding>(); public static final Template.OneArgs EXPRESSION = Template.make("type", (ExpressionType type) -> body( setFuelCost(0), (fuel() <= 0 || RANDOM.nextInt(3) == 0) ? TERMINAL_EXPRESSION.withArgs(type) - : BINARY_OPERATOR_EXPRESSION_BINDING.get().withArgs(type) + : OPERATOR_EXPRESSION_BINDING.get().withArgs(type) )); - private record BinaryOperator(String start, ExpressionType t1, String middle, ExpressionType t2, String end) {} + private interface Operator { + TemplateBody instanciate(); + } - private static final List INT_BINARY_OPERATORS = List.of( + private record BinaryOperator(String prefix, ExpressionType t1, String middle, ExpressionType t2, String postfix) implements Operator { + @Override + public TemplateBody instanciate() { + return body( + prefix, + EXPRESSION.withArgs(t1), + middle, + EXPRESSION.withArgs(t2), + postfix + ); + } + } + + private static final List INT_OPERATORS = List.of( new BinaryOperator("(", ExpressionType.INT, " + ", ExpressionType.INT, ")"), new BinaryOperator("(", ExpressionType.INT, " - ", ExpressionType.INT, ")"), new BinaryOperator("(", ExpressionType.INT, " * ", ExpressionType.INT, ")"), @@ -238,7 +253,7 @@ private record BinaryOperator(String start, ExpressionType t1, String middle, Ex new BinaryOperator("(", ExpressionType.INT, " >>> ", ExpressionType.INT, ")") ); - private static final List LONG_BINARY_OPERATORS = List.of( + private static final List LONG_OPERATORS = List.of( new BinaryOperator("(", ExpressionType.LONG, " + ", ExpressionType.LONG, ")"), new BinaryOperator("(", ExpressionType.LONG, " - ", ExpressionType.LONG, ")"), new BinaryOperator("(", ExpressionType.LONG, " * ", ExpressionType.LONG, ")"), @@ -252,7 +267,7 @@ private record BinaryOperator(String start, ExpressionType t1, String middle, Ex new BinaryOperator("(", ExpressionType.LONG, " >>> ", ExpressionType.LONG, ")") ); - private static final List FLOAT_BINARY_OPERATORS = List.of( + private static final List FLOAT_OPERATORS = List.of( new BinaryOperator("(", ExpressionType.FLOAT, " + ", ExpressionType.FLOAT, ")"), new BinaryOperator("(", ExpressionType.FLOAT, " - ", ExpressionType.FLOAT, ")"), new BinaryOperator("(", ExpressionType.FLOAT, " * ", ExpressionType.FLOAT, ")"), @@ -260,7 +275,7 @@ private record BinaryOperator(String start, ExpressionType t1, String middle, Ex new BinaryOperator("(", ExpressionType.FLOAT, " % ", ExpressionType.FLOAT, ")") ); - private static final List DOUBLE_BINARY_OPERATORS = List.of( + private static final List DOUBLE_OPERATORS = List.of( new BinaryOperator("(", ExpressionType.DOUBLE, " + ", ExpressionType.DOUBLE, ")"), new BinaryOperator("(", ExpressionType.DOUBLE, " - ", ExpressionType.DOUBLE, ")"), new BinaryOperator("(", ExpressionType.DOUBLE, " * ", ExpressionType.DOUBLE, ")"), @@ -268,29 +283,24 @@ private record BinaryOperator(String start, ExpressionType t1, String middle, Ex new BinaryOperator("(", ExpressionType.DOUBLE, " % ", ExpressionType.DOUBLE, ")") ); - private static final List BOOLEAN_BINARY_OPERATORS = List.of( + private static final List BOOLEAN_OPERATORS = List.of( new BinaryOperator("(", ExpressionType.BOOLEAN, " || ", ExpressionType.BOOLEAN, ")"), new BinaryOperator("(", ExpressionType.BOOLEAN, " && ", ExpressionType.BOOLEAN, ")"), new BinaryOperator("(", ExpressionType.BOOLEAN, " ^ ", ExpressionType.BOOLEAN, ")") ); - private static List binaryOperators(ExpressionType type) { + private static List operators(ExpressionType type) { return switch (type) { - case ExpressionType.INT -> INT_BINARY_OPERATORS; - case ExpressionType.LONG -> LONG_BINARY_OPERATORS; - case ExpressionType.FLOAT -> FLOAT_BINARY_OPERATORS; - case ExpressionType.DOUBLE -> DOUBLE_BINARY_OPERATORS; - case ExpressionType.BOOLEAN -> BOOLEAN_BINARY_OPERATORS; + case ExpressionType.INT -> INT_OPERATORS; + case ExpressionType.LONG -> LONG_OPERATORS; + case ExpressionType.FLOAT -> FLOAT_OPERATORS; + case ExpressionType.DOUBLE -> DOUBLE_OPERATORS; + case ExpressionType.BOOLEAN -> BOOLEAN_OPERATORS; }; } - public static final Template.OneArgs BINARY_OPERATOR_EXPRESSION = - Template.make("type", (ExpressionType type) -> let("op", choice(binaryOperators(type)), op -> body( - op.start, - EXPRESSION.withArgs(op.t1), - op.middle, - EXPRESSION.withArgs(op.t2), - op.end - ))); - static { BINARY_OPERATOR_EXPRESSION_BINDING.bind(BINARY_OPERATOR_EXPRESSION); } + public static final Template.OneArgs OPERATOR_EXPRESSION = + Template.make("type", (ExpressionType type) -> choice(operators(type)).instanciate()); + + static { OPERATOR_EXPRESSION_BINDING.bind(OPERATOR_EXPRESSION); } } From 34759d8ed5b4b210871a07973590f9bad46c49b0 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 12:03:58 +0100 Subject: [PATCH 070/212] add unary and ternary operators --- .../lib/template_framework/Library.java | 58 +++++++++++++++++-- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index 73df088a0b327..ff5f2184ce15a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -226,6 +226,17 @@ private interface Operator { TemplateBody instanciate(); } + private record UnaryOperator(String prefix, ExpressionType type, String postfix) implements Operator { + @Override + public TemplateBody instanciate() { + return body( + prefix, + EXPRESSION.withArgs(type), + postfix + ); + } + } + private record BinaryOperator(String prefix, ExpressionType t1, String middle, ExpressionType t2, String postfix) implements Operator { @Override public TemplateBody instanciate() { @@ -239,7 +250,25 @@ public TemplateBody instanciate() { } } + private record TernaryOperator(String prefix, ExpressionType t1, String mid1, ExpressionType t2, String mid2, ExpressionType t3, String postfix) implements Operator { + @Override + public TemplateBody instanciate() { + return body( + prefix, + EXPRESSION.withArgs(t1), + mid1, + EXPRESSION.withArgs(t2), + mid2, + EXPRESSION.withArgs(t3), + postfix + ); + } + } + private static final List INT_OPERATORS = List.of( + new UnaryOperator("(-(", ExpressionType.INT, "))"), + new UnaryOperator("(~", ExpressionType.INT, ")"), + new BinaryOperator("(", ExpressionType.INT, " + ", ExpressionType.INT, ")"), new BinaryOperator("(", ExpressionType.INT, " - ", ExpressionType.INT, ")"), new BinaryOperator("(", ExpressionType.INT, " * ", ExpressionType.INT, ")"), @@ -250,10 +279,15 @@ public TemplateBody instanciate() { new BinaryOperator("(", ExpressionType.INT, " ^ ", ExpressionType.INT, ")"), new BinaryOperator("(", ExpressionType.INT, " << ", ExpressionType.INT, ")"), new BinaryOperator("(", ExpressionType.INT, " >> ", ExpressionType.INT, ")"), - new BinaryOperator("(", ExpressionType.INT, " >>> ", ExpressionType.INT, ")") + new BinaryOperator("(", ExpressionType.INT, " >>> ", ExpressionType.INT, ")"), + + new TernaryOperator("(", ExpressionType.BOOLEAN, " ? ", ExpressionType.INT, " : ", ExpressionType.INT, ")") ); private static final List LONG_OPERATORS = List.of( + new UnaryOperator("(-(", ExpressionType.LONG, "))"), + new UnaryOperator("(~", ExpressionType.LONG, ")"), + new BinaryOperator("(", ExpressionType.LONG, " + ", ExpressionType.LONG, ")"), new BinaryOperator("(", ExpressionType.LONG, " - ", ExpressionType.LONG, ")"), new BinaryOperator("(", ExpressionType.LONG, " * ", ExpressionType.LONG, ")"), @@ -264,29 +298,43 @@ public TemplateBody instanciate() { new BinaryOperator("(", ExpressionType.LONG, " ^ ", ExpressionType.LONG, ")"), new BinaryOperator("(", ExpressionType.LONG, " << ", ExpressionType.LONG, ")"), new BinaryOperator("(", ExpressionType.LONG, " >> ", ExpressionType.LONG, ")"), - new BinaryOperator("(", ExpressionType.LONG, " >>> ", ExpressionType.LONG, ")") + new BinaryOperator("(", ExpressionType.LONG, " >>> ", ExpressionType.LONG, ")"), + + new TernaryOperator("(", ExpressionType.BOOLEAN, " ? ", ExpressionType.LONG, " : ", ExpressionType.LONG, ")") ); private static final List FLOAT_OPERATORS = List.of( + new UnaryOperator("(-(", ExpressionType.FLOAT, "))"), + new BinaryOperator("(", ExpressionType.FLOAT, " + ", ExpressionType.FLOAT, ")"), new BinaryOperator("(", ExpressionType.FLOAT, " - ", ExpressionType.FLOAT, ")"), new BinaryOperator("(", ExpressionType.FLOAT, " * ", ExpressionType.FLOAT, ")"), new BinaryOperator("(", ExpressionType.FLOAT, " / ", ExpressionType.FLOAT, ")"), - new BinaryOperator("(", ExpressionType.FLOAT, " % ", ExpressionType.FLOAT, ")") + new BinaryOperator("(", ExpressionType.FLOAT, " % ", ExpressionType.FLOAT, ")"), + + new TernaryOperator("(", ExpressionType.BOOLEAN, " ? ", ExpressionType.FLOAT, " : ", ExpressionType.FLOAT, ")") ); private static final List DOUBLE_OPERATORS = List.of( + new UnaryOperator("(-(", ExpressionType.DOUBLE, "))"), + new BinaryOperator("(", ExpressionType.DOUBLE, " + ", ExpressionType.DOUBLE, ")"), new BinaryOperator("(", ExpressionType.DOUBLE, " - ", ExpressionType.DOUBLE, ")"), new BinaryOperator("(", ExpressionType.DOUBLE, " * ", ExpressionType.DOUBLE, ")"), new BinaryOperator("(", ExpressionType.DOUBLE, " / ", ExpressionType.DOUBLE, ")"), - new BinaryOperator("(", ExpressionType.DOUBLE, " % ", ExpressionType.DOUBLE, ")") + new BinaryOperator("(", ExpressionType.DOUBLE, " % ", ExpressionType.DOUBLE, ")"), + + new TernaryOperator("(", ExpressionType.BOOLEAN, " ? ", ExpressionType.DOUBLE, " : ", ExpressionType.DOUBLE, ")") ); private static final List BOOLEAN_OPERATORS = List.of( + new UnaryOperator("(!(", ExpressionType.BOOLEAN, "))"), + new BinaryOperator("(", ExpressionType.BOOLEAN, " || ", ExpressionType.BOOLEAN, ")"), new BinaryOperator("(", ExpressionType.BOOLEAN, " && ", ExpressionType.BOOLEAN, ")"), - new BinaryOperator("(", ExpressionType.BOOLEAN, " ^ ", ExpressionType.BOOLEAN, ")") + new BinaryOperator("(", ExpressionType.BOOLEAN, " ^ ", ExpressionType.BOOLEAN, ")"), + + new TernaryOperator("(", ExpressionType.BOOLEAN, " ? ", ExpressionType.BOOLEAN, " : ", ExpressionType.BOOLEAN, ")") ); private static List operators(ExpressionType type) { From 21f721e2011b4f1b0732e54c0db758e2870fb9fc Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 13:38:06 +0100 Subject: [PATCH 071/212] move TODOs around --- .../jtreg/compiler/lib/template_framework/Library.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index ff5f2184ce15a..f56fc769d4a1c 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -46,6 +46,7 @@ * * TODO more operators * TODO more templates + * TODO configure choice and other randomization? */ public abstract class Library { private static final Random RANDOM = Utils.getRandomInstance(); @@ -128,7 +129,7 @@ public enum ExpressionType { case ExpressionType.LONG -> GEN_LONG.next(); case ExpressionType.FLOAT -> GEN_FLOAT.next(); case ExpressionType.DOUBLE -> GEN_DOUBLE.next(); - case ExpressionType.BOOLEAN -> GEN_INT.next() % 2 == 0; // TODO better distribution? + case ExpressionType.BOOLEAN -> GEN_INT.next() % 2 == 0; } )); @@ -183,7 +184,6 @@ public enum ExpressionType { public static final Template.TwoArgs GENERATE_EARLIER_VALUE = Template.make("type", "name", (ExpressionType type, String name) -> body( - // TODO alternatives choice(List.of( intoHook(CLASS_HOOK, DEFINE_STATIC_FIELD.withArgs(type, name)), intoHook(METHOD_HOOK, GENERATE_EARLILER_VALUE_FROM_DELAYED_BOOLEAN.withArgs(type, name)) From 8d5b98c0a8afefbf49612213e1274ad301a921a1 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 13:51:30 +0100 Subject: [PATCH 072/212] rename / comments --- .../lib/template_framework/Library.java | 20 ++++++++++--------- .../examples/TestFuzzExpression.java | 4 ++-- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index f56fc769d4a1c..ab87be3870169 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -129,7 +129,7 @@ public enum ExpressionType { case ExpressionType.LONG -> GEN_LONG.next(); case ExpressionType.FLOAT -> GEN_FLOAT.next(); case ExpressionType.DOUBLE -> GEN_DOUBLE.next(); - case ExpressionType.BOOLEAN -> GEN_INT.next() % 2 == 0; + case ExpressionType.BOOLEAN -> RANDOM.nextInt() % 2 == 0; } )); @@ -138,7 +138,7 @@ public enum ExpressionType { "public static #type #name = ", CONSTANT_EXPRESSION.withArgs(type), ";\n" )); - public static final Template.OneArgs GENERATE_DELAYED_BOOLEAN_USING_BOXING_INLINE = + public static final Template.OneArgs GENERATE_BOOLEAN_USING_BOXING_INLINE = Template.make("name", (String name) -> body( intoHook(CLASS_HOOK, DEFINE_STATIC_FIELD.withArgs(ExpressionType.BOOLEAN, $("flag"))), """ @@ -149,7 +149,7 @@ public enum ExpressionType { """ )); - public static final Template.OneArgs GENERATE_DELAYED_BOOLEAN_USING_EMPTY_LOOP = + public static final Template.OneArgs GENERATE_BOOLEAN_USING_EMPTY_LOOP = Template.make("name", (String name) -> body( """ // #name is constant, but only known after first loop opts, when loop detected as empty. @@ -163,18 +163,20 @@ public enum ExpressionType { """ )); - public static final Template.OneArgs GENERATE_DELAYED_BOOLEAN = + public static final Template.OneArgs GENERATE_BOOLEAN = Template.make("name", (String name) -> body( choice(List.of( - GENERATE_DELAYED_BOOLEAN_USING_BOXING_INLINE.withArgs(name), - GENERATE_DELAYED_BOOLEAN_USING_EMPTY_LOOP.withArgs(name), + // These are expected to constant fold at some point during the compilation: + GENERATE_BOOLEAN_USING_BOXING_INLINE.withArgs(name), + GENERATE_BOOLEAN_USING_EMPTY_LOOP.withArgs(name), + // This will never constant fold, as it just loads a boolean: intoHook(CLASS_HOOK, DEFINE_STATIC_FIELD.withArgs(ExpressionType.BOOLEAN, name)) )) )); - public static final Template.TwoArgs GENERATE_EARLILER_VALUE_FROM_DELAYED_BOOLEAN = + public static final Template.TwoArgs GENERATE_EARLILER_VALUE_FROM_BOOLEAN = Template.make("type", "name", (ExpressionType type, String name) -> body( - GENERATE_DELAYED_BOOLEAN.withArgs($("delayed")), + GENERATE_BOOLEAN.withArgs($("delayed")), "#type #name = ($delayed) ? ", CONSTANT_EXPRESSION.withArgs(type), " : ", @@ -186,7 +188,7 @@ public enum ExpressionType { Template.make("type", "name", (ExpressionType type, String name) -> body( choice(List.of( intoHook(CLASS_HOOK, DEFINE_STATIC_FIELD.withArgs(type, name)), - intoHook(METHOD_HOOK, GENERATE_EARLILER_VALUE_FROM_DELAYED_BOOLEAN.withArgs(type, name)) + intoHook(METHOD_HOOK, GENERATE_EARLILER_VALUE_FROM_BOOLEAN.withArgs(type, name)) )) )); diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java index 02dc1972a3af6..d2cb20fafe695 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java @@ -53,8 +53,8 @@ import static compiler.lib.template_framework.Library.EXPRESSION; /** - * This is a basic IR verification test, in combination with Generators for random input generation - * and Verify for output verification. + * This is a basic expression fuzzer: it generates random expressions using {@link Library.Expression}, + * and verifies the results using {@link Verify.checkEQ}. *

* The "@compile" command for JTREG is required so that the frameworks used in the Template code * are compiled and available for the Test-VM. From 85d8b0219912fd5c5948360c53135ab2b85fb8c8 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 13:55:45 +0100 Subject: [PATCH 073/212] rename test --- .../examples/{TestArguments.java => TestSimple.java} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename test/hotspot/jtreg/testlibrary_tests/template_framework/examples/{TestArguments.java => TestSimple.java} (89%) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestArguments.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestSimple.java similarity index 89% rename from test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestArguments.java rename to test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestSimple.java index 0833bc584878c..127c89deece9d 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestArguments.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestSimple.java @@ -23,10 +23,10 @@ /* * @test - * @summary Test templates with arguments. + * @summary Test simple use of Templates with the Compile Framework. * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run driver template_framework.examples.TestArguments + * @run driver template_framework.examples.TestSimple */ package template_framework.examples; @@ -35,7 +35,7 @@ import compiler.lib.template_framework.Template; import static compiler.lib.template_framework.Template.body; -public class TestArguments { +public class TestSimple { public static void main(String[] args) { // Create a new CompileFramework instance. @@ -60,7 +60,7 @@ public static void main(String[] args) { // Generate a source Java file as String public static String generate() { // Create a Template with two arguments. - var template = Template.make("arg1", "arg2", (String arg1, String arg2) -> body( + var template = Template.make("arg1", "arg2", (Integer arg1, String arg2) -> body( """ package p.xyz; public class InnerTest { @@ -72,6 +72,6 @@ public static int test() { )); // Use the template with two arguments, and render it to a String. - return template.withArgs("42", "7").render(); + return template.withArgs(42, "7").render(); } } From fd66de7865ce3cdec9f3249e0f4d92d37015055d Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 14:17:53 +0100 Subject: [PATCH 074/212] TestAdvanced first part --- .../examples/TestAdvanced.java | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java new file mode 100644 index 0000000000000..04fb13213ffcf --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java @@ -0,0 +1,92 @@ +/* + * 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 + * @summary Test advanced use of Templates with the Compile Framework. + * It displays the use of most features in the Template Framework. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver template_framework.examples.TestAdvanced + */ + +package template_framework.examples; + +import compiler.lib.compile_framework.*; +import compiler.lib.template_framework.Template; +import static compiler.lib.template_framework.Template.body; + +public class TestAdvanced { + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("p.xyz.InnerTest1", generate1()); + + // Compile the source file. + comp.compile(); + + // Object ret = p.xyz.InnterTest1.main(); + comp.invoke("p.xyz.InnerTest1", "main", new Object[] {}); + } + + // Generate a source Java file as String + public static String generate1() { + // A Template with no arguments. + var templateHello = Template.make(() -> body( + """ + System.out.println("Hello"); + """ + )); + + // A Template with a single Integer argument. + var templateCompare = Template.make("arg", (Integer arg) -> body( + "System.out.println(", arg, ");\n", // capture arg via lambda argument + "System.out.println(#arg);\n", // capture arg via hashtag replacement + "if (#arg != ", arg, ") { throw new RuntimeException(\"mismatch\"); }\n" + )); + + // A Template that creates the body of the Class and main method, and then + // uses the two Templates above inside it. + var templateClass = Template.make(() -> body( + """ + package p.xyz; + + public class InnerTest1 { + public static void main() { + """, + templateHello.withArgs(), + templateCompare.withArgs(7), + templateCompare.withArgs(42), + """ + } + } + """ + )); + + // Render templateClass to String. + return templateClass.withArgs().render(); + } +} From 42f4a5312bafa1a3dfea5a16e6c47dc4a339634f Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 15:01:36 +0100 Subject: [PATCH 075/212] more examples --- .../examples/TestAdvanced.java | 124 +++++++++++++++++- 1 file changed, 120 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java index 04fb13213ffcf..7ebf4e30f477b 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java @@ -32,9 +32,13 @@ package template_framework.examples; +import java.util.List; + import compiler.lib.compile_framework.*; import compiler.lib.template_framework.Template; import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.let; +import static compiler.lib.template_framework.Template.$; public class TestAdvanced { @@ -42,18 +46,57 @@ public static void main(String[] args) { // Create a new CompileFramework instance. CompileFramework comp = new CompileFramework(); - // Add a java source file. + // Add java source files. comp.addJavaSourceCode("p.xyz.InnerTest1", generate1()); + comp.addJavaSourceCode("p.xyz.InnerTest2", generate2()); + comp.addJavaSourceCode("p.xyz.InnerTest3", generate3()); - // Compile the source file. + // Compile the source files. comp.compile(); // Object ret = p.xyz.InnterTest1.main(); comp.invoke("p.xyz.InnerTest1", "main", new Object[] {}); + comp.invoke("p.xyz.InnerTest2", "main", new Object[] {}); + comp.invoke("p.xyz.InnerTest3", "main", new Object[] {}); } - // Generate a source Java file as String + // This example shows the use of various Tokens. public static String generate1() { + // A Template is essencially a function / lambda that produces a + // token body, which is a list of Tokens that are concatenated. + var templateClass = Template.make(() -> body( + // The "body" method is filled by a sequence of "Tokens". + // This can be Strings and multi-line Strings, but also + // boxed primitives. + """ + package p.xyz; + + public class InnerTest1 { + public static void main() { + System.out.println("Hello World!"); + """, + "int a = ", Integer.valueOf(1), ";\n", + "float b = ", Float.valueOf(1.5f), ";\n", + // Special Float values are "smartly" formatted! + "float nan = ", Float.valueOf(Float.POSITIVE_INFINITY), ";\n", + "boolean c = ", Boolean.valueOf(true), ";\n", + // Lists of Tokens are also allowed: + List.of("int ", "d = 5", ";\n"), + // That can be great for streaming / mapping over an existing list: + List.of(3, 5, 7, 11).stream().map(i -> "System.out.println(" + i + ");\n").toList(), + """ + System.out.println(a + " " + b + " " + nan + " " + c + " " + d); + } + } + """ + )); + + // Render templateClass to String. + return templateClass.withArgs().render(); + } + + // This example shows the use of Templates, with and without arguments. + public static String generate2() { // A Template with no arguments. var templateHello = Template.make(() -> body( """ @@ -74,7 +117,7 @@ public static String generate1() { """ package p.xyz; - public class InnerTest1 { + public class InnerTest2 { public static void main() { """, templateHello.withArgs(), @@ -89,4 +132,77 @@ public static void main() { // Render templateClass to String. return templateClass.withArgs().render(); } + + // Example with hashtag replacements (arguments and let), and $-name renamings. + // Note: hashtag replacements are a workaround for the missing string templates. + // If we had string templates, we could just capture the typed lambda + // arguments, and use them directly in the String via string templating. + public static String generate3() { + var template1 = Template.make("x", (Integer x) -> body( + // We have the "#x" hashtag replacement from the argument capture above. + // Additionally, we can define "#con" as a hashtag replacement from let: + let("con", 3 * x), + // In the code below, we use "var" as a local variable. But if we were + // to instantiate this template twice, the names could conflict. Hence, + // we automatically rename the names that have a $ prepended. + """ + int $var = #con; + System.out.println("T1: #x, #con, " + $var); + """ + )); + + var template2 = Template.make("x", (Integer x) -> + // Sometimes it can be helpful to not just create a hashtag replacement + // with let, but also to capture the variable. + let("y", 11 * x, y -> + body( + """ + System.out.println("T2: #x, #y"); + """, + template1.withArgs(y) + ) + ) + ); + + // This template generates an int variable and assigns it a value. + // Together with template4, we see that each template has a unique renaming + // for a $-name replacement. + var template3 = Template.make("name", "value", (String name, Integer value) -> body( + """ + int #name = #value; // Note: $var is not #name + """ + )); + + var template4 = Template.make(() -> body( + """ + // We will define the variable $var: + """, + // We can capture the $-name programmatically, and pass it to other templates: + template3.withArgs($("var"), 42), + """ + if ($var != 42) { throw new RuntimeException("Wrong value!"); } + """ + )); + + var templateClass = Template.make(() -> body( + """ + package p.xyz; + + public class InnerTest3 { + public static void main() { + """, + template1.withArgs(1), + template1.withArgs(7), + template2.withArgs(2), + template2.withArgs(5), + template4.withArgs(), + """ + } + } + """ + )); + + // Render templateClass to String. + return templateClass.withArgs().render(); + } } From 231d6d7ec1425dde80f1281e4afbbf5977200061 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 15:23:48 +0100 Subject: [PATCH 076/212] example 4 with hooks --- .../examples/TestAdvanced.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java index 7ebf4e30f477b..cc7ea9351c220 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java @@ -36,9 +36,11 @@ import compiler.lib.compile_framework.*; import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.Hook; import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; +import static compiler.lib.template_framework.Template.intoHook; public class TestAdvanced { @@ -50,6 +52,7 @@ public static void main(String[] args) { comp.addJavaSourceCode("p.xyz.InnerTest1", generate1()); comp.addJavaSourceCode("p.xyz.InnerTest2", generate2()); comp.addJavaSourceCode("p.xyz.InnerTest3", generate3()); + comp.addJavaSourceCode("p.xyz.InnerTest4", generate4()); // Compile the source files. comp.compile(); @@ -58,6 +61,7 @@ public static void main(String[] args) { comp.invoke("p.xyz.InnerTest1", "main", new Object[] {}); comp.invoke("p.xyz.InnerTest2", "main", new Object[] {}); comp.invoke("p.xyz.InnerTest3", "main", new Object[] {}); + comp.invoke("p.xyz.InnerTest4", "main", new Object[] {}); } // This example shows the use of various Tokens. @@ -205,4 +209,58 @@ public static void main() { // Render templateClass to String. return templateClass.withArgs().render(); } + + // In this example, we look at the use of Hooks. + public static String generate4() { + // We can define a custom hook. + // Note: generally we prefer using the pre-defined CLASS_HOOK and METHOD_HOOK from the Library, + // when ever possible. See also the example after this one. + var myHook = new Hook("MyHook"); + + var template1 = Template.make("name", "value", (String name, Integer value) -> body( + """ + public static int #name = #value; + """ + )); + + var template2 = Template.make("x", (Integer x) -> body( + """ + // Let us go back to the hook, and define a field named $field... + """, + intoHook(myHook, template1.withArgs($("field"), x)), + """ + System.out.println("$field: " + $field); + if ($field != #x) { throw new RuntimeException("Wrong value!"); } + """ + )); + + var templateClass = Template.make(() -> body( + """ + package p.xyz; + + public class InnerTest4 { + """, + // We set a Hook outside the main method, but inside the Class. + // The Hook is set for the Tokens inside the set braces. + // As long as the hook is set, we can generate code into the hook, + // here we can define static fields for example. + myHook.set( + """ + public static void main() { + """, + template2.withArgs(5), + template2.withArgs(7), + """ + } + """ + ), + """ + } + """ + )); + + // Render templateClass to String. + return templateClass.withArgs().render(); + } + } From 26e28168caf1f7ac2830131c5a0ed445016c52d9 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 15:56:26 +0100 Subject: [PATCH 077/212] more hook examples --- .../examples/TestAdvanced.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java index cc7ea9351c220..b32a7dc7da490 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java @@ -41,6 +41,8 @@ import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; import static compiler.lib.template_framework.Template.intoHook; +import static compiler.lib.template_framework.Library.CLASS_HOOK; +import static compiler.lib.template_framework.Library.METHOD_HOOK; public class TestAdvanced { @@ -53,6 +55,7 @@ public static void main(String[] args) { comp.addJavaSourceCode("p.xyz.InnerTest2", generate2()); comp.addJavaSourceCode("p.xyz.InnerTest3", generate3()); comp.addJavaSourceCode("p.xyz.InnerTest4", generate4()); + comp.addJavaSourceCode("p.xyz.InnerTest5", generate5()); // Compile the source files. comp.compile(); @@ -62,6 +65,7 @@ public static void main(String[] args) { comp.invoke("p.xyz.InnerTest2", "main", new Object[] {}); comp.invoke("p.xyz.InnerTest3", "main", new Object[] {}); comp.invoke("p.xyz.InnerTest4", "main", new Object[] {}); + comp.invoke("p.xyz.InnerTest5", "main", new Object[] {}); } // This example shows the use of various Tokens. @@ -263,4 +267,85 @@ public static void main() { return templateClass.withArgs().render(); } + // We saw the use of custom hooks above, but now we look at the use of CLASS_HOOK and METHOD_HOOK + // from the Temlate Library. + public static String generate5() { + var templateStaticField = Template.make("name", "value", (String name, Integer value) -> body( + """ + static { System.out.println("Defining static field #name"); } + public static int #name = #value; + """ + )); + + var templateLocalVariable = Template.make("name", "value", (String name, Integer value) -> body( + """ + System.out.println("Defining local variable #name"); + int #name = #value; + """ + )); + + var templateMethodBody = Template.make(() -> body( + """ + // Let's define a local variable $var and a static field $field. + """, + intoHook(CLASS_HOOK, templateStaticField.withArgs($("field"), 5)), + intoHook(METHOD_HOOK, templateLocalVariable.withArgs($("var"), 11)), + """ + System.out.println("$field: " + $field); + System.out.println("$var: " + $var); + if ($field * $var != 55) { throw new RuntimeException("Wrong value!"); } + """ + )); + + var templateClass = Template.make(() -> body( + """ + package p.xyz; + + public class InnerTest5 { + """, + // Class Hook for fields. + CLASS_HOOK.set( + """ + public static void main() { + """, + // Method Hook for local variables, and earlier computations. + METHOD_HOOK.set( + """ + // This is the beginning of the "main" method body. + System.out.println("Welcome to main!"); + """, + templateMethodBody.withArgs(), + """ + System.out.println("Going to call other..."); + other(); + """ + ), + """ + } + + private static void other() { + """, + // Have a separate method hook for other, so that it can generate + // its own local variables. + METHOD_HOOK.set( + """ + System.out.println("Welcome to other!"); + """, + templateMethodBody.withArgs(), + """ + System.out.println("Done with other."); + """ + ), + """ + } + """ + ), + """ + } + """ + )); + + // Render templateClass to String. + return templateClass.withArgs().render(); + } } From ea31f233fceb8022f0bbb9cea333918ef3d43741 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 16:56:10 +0100 Subject: [PATCH 078/212] more examples and some other changes --- .../lib/template_framework/CodeFrame.java | 7 +- .../lib/template_framework/Library.java | 5 +- .../lib/template_framework/NameSelection.java | 2 +- .../lib/template_framework/Template.java | 3 - .../{TestAdvanced.java => TestTutorial.java} | 199 ++++++++++++++++-- .../tests/TestTemplate.java | 4 +- 6 files changed, 198 insertions(+), 22 deletions(-) rename test/hotspot/jtreg/testlibrary_tests/template_framework/examples/{TestAdvanced.java => TestTutorial.java} (62%) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java index 88d34b70f0d8d..e51bea1562b36 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java @@ -94,7 +94,12 @@ private NameSet nameSet(NameSelection nameSelection) { } void defineName(String name, Object type, NameSelection nameSelection) { - nameSet(nameSelection).add(name, type); + if (nameSelection == NameSelection.MUTABLE) { + mutableNames.add(name, type); + allNames.add(name, type); + } else { + allNames.add(name, type); + } } int countNames(Object type, NameSelection nameSelection) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index ab87be3870169..9a56977638268 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -38,8 +38,8 @@ import static compiler.lib.template_framework.Template.setFuelCost; import static compiler.lib.template_framework.Template.countNames; import static compiler.lib.template_framework.Template.sampleName; -import static compiler.lib.template_framework.Template.ALL; -import static compiler.lib.template_framework.Template.MUTABLE; +import static compiler.lib.template_framework.NameSelection.ALL; +import static compiler.lib.template_framework.NameSelection.MUTABLE; /** * The Library provides a collection of helpful Templates and Hooks. @@ -196,6 +196,7 @@ public enum ExpressionType { Template.make("type", (ExpressionType type) -> { if (countNames(type, ALL) == 0 || RANDOM.nextInt(5) == 0) { return body( + // TODO defineName !!! GENERATE_EARLIER_VALUE.withArgs(type, $("early")), $("early") ); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/NameSelection.java b/test/hotspot/jtreg/compiler/lib/template_framework/NameSelection.java index 740ce208727c8..b85cb965be38a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/NameSelection.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/NameSelection.java @@ -23,4 +23,4 @@ package compiler.lib.template_framework; -enum NameSelection { MUTABLE, ALL } +public enum NameSelection { MUTABLE, ALL } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 7173641740157..fa45616a98a51 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -102,9 +102,6 @@ static NothingToken setFuelCost(float fuelCost) { return new NothingToken(); } - static final NameSelection MUTABLE = NameSelection.MUTABLE; - static final NameSelection ALL = NameSelection.ALL; - static NothingToken defineName(String name, Object type, NameSelection nameSelection) { Renderer.getCurrent().defineName(name, type, nameSelection); return new NothingToken(); diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java similarity index 62% rename from test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java rename to test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java index b32a7dc7da490..7dcd8a3fe9f22 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java @@ -23,39 +23,49 @@ /* * @test - * @summary Test advanced use of Templates with the Compile Framework. + * @summary Demonstrate the use of Templates with the Compile Framework. * It displays the use of most features in the Template Framework. * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run driver template_framework.examples.TestAdvanced + * @run driver template_framework.examples.TestTutorial */ package template_framework.examples; +import java.util.Collections; import java.util.List; import compiler.lib.compile_framework.*; import compiler.lib.template_framework.Template; import compiler.lib.template_framework.Hook; +import compiler.lib.template_framework.TemplateBinding; import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; import static compiler.lib.template_framework.Template.intoHook; +import static compiler.lib.template_framework.Template.fuel; +import static compiler.lib.template_framework.Template.defineName; +import static compiler.lib.template_framework.Template.sampleName; +import static compiler.lib.template_framework.Template.countNames; import static compiler.lib.template_framework.Library.CLASS_HOOK; import static compiler.lib.template_framework.Library.METHOD_HOOK; +import static compiler.lib.template_framework.NameSelection.MUTABLE; +import static compiler.lib.template_framework.NameSelection.ALL; -public class TestAdvanced { +public class TestTutorial { public static void main(String[] args) { // Create a new CompileFramework instance. CompileFramework comp = new CompileFramework(); // Add java source files. - comp.addJavaSourceCode("p.xyz.InnerTest1", generate1()); - comp.addJavaSourceCode("p.xyz.InnerTest2", generate2()); - comp.addJavaSourceCode("p.xyz.InnerTest3", generate3()); - comp.addJavaSourceCode("p.xyz.InnerTest4", generate4()); - comp.addJavaSourceCode("p.xyz.InnerTest5", generate5()); + comp.addJavaSourceCode("p.xyz.InnerTest1", generateWithListOfTokens()); + comp.addJavaSourceCode("p.xyz.InnerTest2", generateWithTemplateArguments()); + comp.addJavaSourceCode("p.xyz.InnerTest3", generateWithHashtagAndDollarReplacements()); + comp.addJavaSourceCode("p.xyz.InnerTest4", generateWithCustomHooks()); + comp.addJavaSourceCode("p.xyz.InnerTest5", generateWithLibraryHooks()); + comp.addJavaSourceCode("p.xyz.InnerTest6", generateWithRecursionAndBindingsAndFuel()); + comp.addJavaSourceCode("p.xyz.InnerTest7", generateWithNames()); // Compile the source files. comp.compile(); @@ -66,10 +76,12 @@ public static void main(String[] args) { comp.invoke("p.xyz.InnerTest3", "main", new Object[] {}); comp.invoke("p.xyz.InnerTest4", "main", new Object[] {}); comp.invoke("p.xyz.InnerTest5", "main", new Object[] {}); + comp.invoke("p.xyz.InnerTest6", "main", new Object[] {}); + comp.invoke("p.xyz.InnerTest7", "main", new Object[] {}); } // This example shows the use of various Tokens. - public static String generate1() { + public static String generateWithListOfTokens() { // A Template is essencially a function / lambda that produces a // token body, which is a list of Tokens that are concatenated. var templateClass = Template.make(() -> body( @@ -104,7 +116,7 @@ public static void main() { } // This example shows the use of Templates, with and without arguments. - public static String generate2() { + public static String generateWithTemplateArguments() { // A Template with no arguments. var templateHello = Template.make(() -> body( """ @@ -145,7 +157,7 @@ public static void main() { // Note: hashtag replacements are a workaround for the missing string templates. // If we had string templates, we could just capture the typed lambda // arguments, and use them directly in the String via string templating. - public static String generate3() { + public static String generateWithHashtagAndDollarReplacements() { var template1 = Template.make("x", (Integer x) -> body( // We have the "#x" hashtag replacement from the argument capture above. // Additionally, we can define "#con" as a hashtag replacement from let: @@ -215,7 +227,7 @@ public static void main() { } // In this example, we look at the use of Hooks. - public static String generate4() { + public static String generateWithCustomHooks() { // We can define a custom hook. // Note: generally we prefer using the pre-defined CLASS_HOOK and METHOD_HOOK from the Library, // when ever possible. See also the example after this one. @@ -269,7 +281,7 @@ public static void main() { // We saw the use of custom hooks above, but now we look at the use of CLASS_HOOK and METHOD_HOOK // from the Temlate Library. - public static String generate5() { + public static String generateWithLibraryHooks() { var templateStaticField = Template.make("name", "value", (String name, Integer value) -> body( """ static { System.out.println("Defining static field #name"); } @@ -348,4 +360,165 @@ private static void other() { // Render templateClass to String. return templateClass.withArgs().render(); } + + // This example shows the use of bindings to allow cyclic references of Templates, + // allowing recursive template generation. We also show the use of fuel to limit + // recursion. + public static String generateWithRecursionAndBindingsAndFuel() { + // Binding allows the use of template1 inside of template1, via the binding indirection. + var binding1 = new TemplateBinding>(); + var template1 = Template.make("depth", (Integer depth) -> body( + let("fuel", fuel()), + """ + System.out.println("At depth #depth with fuel #fuel."); + """, + // We cannot yet use template1 directly, as it is being defined. + // So we use binding1 instead. + // For every recursion depth, some fuel is automatically subtracted + // so that the fuel slowly depletes with the depth. + // We keep the recursion going until the fuel is depleted. + (fuel() > 0) ? binding1.get().withArgs(depth + 1) + : "System.out.println(\"Fuel depleted.\");\n", + """ + System.out.println("Exit depth #depth."); + """ + )); + binding1.bind(template1); + + var templateClass = Template.make(() -> body( + """ + package p.xyz; + + public class InnerTest6 { + public static void main() { + System.out.println("Welcome to main!"); + """, + template1.withArgs(0), + """ + } + } + """ + )); + + // Render templateClass to String. + return templateClass.withArgs().render(); + } + + // Example with names, i.e. defineName, countNames, and sampleName. + // These can be used to define and sample variables from outer scopes. + public static String generateWithNames() { + var templateSample = Template.make("type", (Object type) -> body( + let("name", sampleName(type, ALL)), + """ + System.out.println("Sampling type #type: #name = " + #name); + """ + )); + + var templateStaticField = Template.make("type", (Object type) -> body( + defineName($("field"), type, MUTABLE), + """ + public static #type $field = 0; + """ + )); + + var templateLocalVariable = Template.make("type", (Object type) -> body( + defineName($("var"), type, MUTABLE), + """ + #type $var = 0; + """ + )); + + var templateStatus = Template.make(() -> body( + let("ints", countNames("int", ALL)), + let("longs", countNames("long", ALL)), + """ + System.out.println("Status: #ints ints, #longs longs."); + """ + )); + + var templateMain = Template.make(() -> body( + """ + System.out.println("Starting inside main..."); + """, + templateStatus.withArgs(), + intoHook(METHOD_HOOK, templateLocalVariable.withArgs("int")), + intoHook(METHOD_HOOK, templateLocalVariable.withArgs("long")), + intoHook(CLASS_HOOK, templateStaticField.withArgs("int")), + intoHook(CLASS_HOOK, templateStaticField.withArgs("long")), + templateStatus.withArgs(), + // We should see a mix if fields and variables sampled. + Collections.nCopies(5, templateSample.withArgs("int")), + Collections.nCopies(5, templateSample.withArgs("long")), + templateStatus.withArgs(), + """ + System.out.println("Finishing inside main."); + """ + )); + + var templateOther = Template.make(() -> body( + """ + System.out.println("Starting inside other..."); + """, + templateStatus.withArgs(), + // We still have all the field definitions from main. + Collections.nCopies(5, templateSample.withArgs("int")), + Collections.nCopies(5, templateSample.withArgs("long")), + templateStatus.withArgs(), + """ + System.out.println("Finishing inside other."); + """ + )); + + var templateClass = Template.make(() -> body( + """ + package p.xyz; + + public class InnerTest7 { + """, + // Class Hook for fields. + CLASS_HOOK.set( + """ + public static void main() { + """, + // Method Hook for local variables, and earlier computations. + METHOD_HOOK.set( + """ + // This is the beginning of the "main" method body. + System.out.println("Welcome to main!"); + """, + templateMain.withArgs(), + """ + System.out.println("Going to call other..."); + other(); + """ + ), + """ + } + + private static void other() { + """, + // Have a separate method hook for other, so that it can generate + // its own local variables. + METHOD_HOOK.set( + """ + System.out.println("Welcome to other!"); + """, + templateOther.withArgs(), + """ + System.out.println("Done with other."); + """ + ), + """ + } + """ + ), + """ + } + """ + )); + + // Render templateClass to String. + return templateClass.withArgs().render(); + } + } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index c0f27898071d4..3c81166ea0ad6 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -48,8 +48,8 @@ import static compiler.lib.template_framework.Template.defineName; import static compiler.lib.template_framework.Template.countNames; import static compiler.lib.template_framework.Template.sampleName; -import static compiler.lib.template_framework.Template.MUTABLE; -import static compiler.lib.template_framework.Template.ALL; +import static compiler.lib.template_framework.NameSelection.MUTABLE; +import static compiler.lib.template_framework.NameSelection.ALL; public class TestTemplate { interface FailingTest { From 93989e0d4f8b76d25b3d4bb86ae74499018b9a01 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 17:02:16 +0100 Subject: [PATCH 079/212] follow up test fix --- .../tests/TestTemplate.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 3c81166ea0ad6..ad3d33d4efb35 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -37,8 +37,10 @@ import java.util.List; import java.util.HashSet; -import compiler.lib.template_framework.*; - +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.Hook; +import compiler.lib.template_framework.TemplateBinding; +import compiler.lib.template_framework.RendererException; import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.intoHook; import static compiler.lib.template_framework.Template.$; @@ -943,7 +945,7 @@ class X_1 { define immutable int name_4 [int: 0 and 1] define mutable int name_9 - [int: 1 and 1] + [int: 1 and 2] begin body_1 [int: 0 and 0] start with immutable @@ -963,7 +965,7 @@ class X_1 { { sample_12 name_9 = 7 } sample_12 - [int: 1 and 1] + [int: 1 and 2] [int: 0 and 0] } """; @@ -1000,14 +1002,14 @@ public static void testNames3() { String expected = """ class Y_1 { - [int: 1 and 1] + [int: 1 and 2] begin body_1 - [int: 1 and 1] + [int: 1 and 2] define mutable - [int: 1 and 1] + [int: 1 and 2] define immutable - [int: 1 and 1] - [int: 1 and 1] + [int: 1 and 2] + [int: 1 and 2] } """; checkEQ(code, expected); From 53a06f2a20e204069b11bbab288b4254de6e93d4 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Feb 2025 18:08:37 +0100 Subject: [PATCH 080/212] improve documentationo --- .../template_framework/TemplateWithArgs.java | 77 +++++++++++++++---- 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java index 927c0b8f3670a..dff9d183e205a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java @@ -23,12 +23,26 @@ package compiler.lib.template_framework; -public sealed interface TemplateWithArgs extends Token - permits TemplateWithArgs.ZeroArgsUse, - TemplateWithArgs.OneArgsUse, - TemplateWithArgs.TwoArgsUse +/** + * Represents a {@link Template} with applied arguments, ready for instantiation, either + * as a {@link Token} inside another {@link Template} or with {@link render}. + */ +public sealed abstract class TemplateWithArgs implements Token + permits TemplateWithArgs.ZeroArgsUse, + TemplateWithArgs.OneArgsUse, + TemplateWithArgs.TwoArgsUse { - record ZeroArgsUse(Template.ZeroArgs zeroArgs) implements TemplateWithArgs, Token { + /** + * Represents a zero-argument {@link Template} with applied arguments, ready for instantiation + * either as a {@link Token} inside another {@link Template} or with {@link render}. + */ + public static final class ZeroArgsUse extends TemplateWithArgs implements Token { + private final Template.ZeroArgs zeroArgs; + + ZeroArgsUse(Template.ZeroArgs zeroArgs) { + this.zeroArgs = zeroArgs; + } + @Override public TemplateBody instantiate() { return zeroArgs.instantiate(); @@ -38,19 +52,45 @@ public TemplateBody instantiate() { public void visitArguments(ArgumentVisitor visitor) {} } - record OneArgsUse(Template.OneArgs oneArg, A a) implements TemplateWithArgs, Token { + /** + * Represents a one-argument {@link Template} with applied arguments, ready for instantiation + * either as a {@link Token} inside another {@link Template} or with {@link render}. + */ + public static final class OneArgsUse extends TemplateWithArgs implements Token { + private final Template.OneArgs oneArgs; + private final A a; + + OneArgsUse(Template.OneArgs oneArgs, A a) { + this.oneArgs = oneArgs; + this.a = a; + } + @Override public TemplateBody instantiate() { - return oneArg.instantiate(a); + return oneArgs.instantiate(a); } @Override public void visitArguments(ArgumentVisitor visitor) { - visitor.visit(oneArg.arg0Name(), a); + visitor.visit(oneArgs.arg0Name(), a); } } - record TwoArgsUse(Template.TwoArgs twoArgs, A a, B b) implements TemplateWithArgs, Token { + /** + * Represents a two-argument {@link Template} with applied arguments, ready for instantiation + * either as a {@link Token} inside another {@link Template} or with {@link render}. + */ + public static final class TwoArgsUse extends TemplateWithArgs implements Token { + private final Template.TwoArgs twoArgs; + private final A a; + private final B b; + + TwoArgsUse(Template.TwoArgs twoArgs, A a, B b) { + this.twoArgs = twoArgs; + this.a = a; + this.b = b; + } + @Override public TemplateBody instantiate() { return twoArgs.instantiate(a, b); @@ -63,20 +103,31 @@ public void visitArguments(ArgumentVisitor visitor) { } } - TemplateBody instantiate(); + abstract TemplateBody instantiate(); @FunctionalInterface interface ArgumentVisitor { void visit(String name, Object value); } - void visitArguments(ArgumentVisitor visitor); + abstract void visitArguments(ArgumentVisitor visitor); - default String render() { + /** + * Renders the {@link Template} with applied arguments to a {@link String}. + * + * @return The {@link Template} rendered to a {@link String}. + */ + public final String render() { return Renderer.render(this); } - default String render(float fuel) { + /** + * Renders the {@link Template} with applied arguments to a {@link String}. + * + * @param fuel The amount of fuel provided for recursive {@link Template} instantiations. + * @return The {@link Template} rendered to a {@link String}. + */ + public final String render(float fuel) { return Renderer.render(this, fuel); } } From 3325e11702ed0ab175eb5c1084f6b4a76a27c777 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 17 Feb 2025 08:25:16 +0100 Subject: [PATCH 081/212] more documentation --- .../template_framework/TemplateBinding.java | 27 +++++++++++++++++++ .../lib/template_framework/TemplateBody.java | 4 +++ .../template_framework/TemplateWithArgs.java | 2 ++ 3 files changed, 33 insertions(+) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBinding.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBinding.java index d3a1ad42e8f44..c077fd76381a0 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBinding.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBinding.java @@ -23,9 +23,29 @@ package compiler.lib.template_framework; +/** + * To facilitate recursive uses of {@link Template}s, e.g. where a template uses + * itself, where a template needs to be referenced before it is fully defined, + * one can use the indirection of a {@link TemplateBinding}. The {@link TemplateBinding} + * is allocated first without any {@link Template} bound to it yet. At this stage, + * it can be used with {@link get} inside a {@link Template}. Later, we can {@link bind} + * a {@link Template} to the binding, such that {@link get} returns that bound + * {@link Template}. + */ public class TemplateBinding { private T template = null; + /** + * Creates a new {@link TemplateBinding} that has no {@link Template} bound to it yet. + */ + public TemplateBinding() {} + + /** + * Retrieve the {@link Template} that was previously bound to the binding. + * + * @return The {@link Template} that was previously bound with {@link bind}. + * @throws RendererException if no {@link Template} was bound yet. + */ public T get() { if (template == null) { throw new RendererException("Cannot 'get' before 'bind'."); @@ -33,6 +53,13 @@ public T get() { return template; } + + /** + * Binds a {@link Template} for future reference using {@link get}. + * + * @param template The {@link Template} to be bound. + * @throws RendererException if a {@link Template} was already bound. + */ public void bind(T template) { if (this.template != null) { throw new RendererException("Duplicate 'bind' not allowed."); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBody.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBody.java index 9a0639e0c3d0a..b30f10e8ce45d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBody.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBody.java @@ -25,4 +25,8 @@ import java.util.List; +/** + * A {@link Template} generates a {@link TemplateBody}, which is a list of {@link Token}s, + * which are then later rendered to {@link String}s. + */ public record TemplateBody(List tokens) {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java index dff9d183e205a..814db65aae9dc 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java @@ -32,6 +32,8 @@ public sealed abstract class TemplateWithArgs implements Token TemplateWithArgs.OneArgsUse, TemplateWithArgs.TwoArgsUse { + private TemplateWithArgs() {} + /** * Represents a zero-argument {@link Template} with applied arguments, ready for instantiation * either as a {@link Token} inside another {@link Template} or with {@link render}. From f76492e39c8b5f2fe3716560303a398a393f77a5 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 17 Feb 2025 13:49:07 +0100 Subject: [PATCH 082/212] some more docs --- .../lib/template_framework/Template.java | 154 ++++++++++++++++-- 1 file changed, 139 insertions(+), 15 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index fa45616a98a51..8cbd01e4c29fd 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -26,30 +26,73 @@ import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; +import java.util.List; +/** + * TODO + * TODO talk about hashtag replacement and arguments. + */ public interface Template { - static ZeroArgs make(Supplier t) { - return new ZeroArgs(t); - } - - static OneArgs make(String arg0Name, Function t) { - return new OneArgs<>(arg0Name, t); - } - - static TwoArgs make(String arg0Name, String arg1Name, BiFunction t) { - return new TwoArgs<>(arg0Name, arg1Name, t); - } + /** + * Creates a {@link Template} with no arguments. + * + * @param body The {@link TemplateBody} created by {@link Template#body}. + * @return A {@link Template} with zero arguments. + */ + static ZeroArgs make(Supplier body) { + return new ZeroArgs(body); + } + + /** + * Creates a {@link Template} with one argument. + * + * @param body The {@link TemplateBody} created by {@link Template#body}. + * @param Type of the zeroth argument. + * @param arg0Name The name of the zeroth argument for hashtag replacement. + * @return A {@link Template} with one argument. + */ + static OneArgs make(String arg0Name, Function body) { + return new OneArgs<>(arg0Name, body); + } + + /** + * Creates a {@link Template} with two arguments. + * + * @param body The {@link TemplateBody} created by {@link Template#body}. + * @param Type of the zeroth argument. + * @param arg0Name The name of the zeroth argument for hashtag replacement. + * @param Type of the first argument. + * @param arg1Name The name of the first argument for hashtag replacement. + * @return A {@link Template} with two arguments. + */ + static TwoArgs make(String arg0Name, String arg1Name, BiFunction body) { + return new TwoArgs<>(arg0Name, arg1Name, body); + } + + /** + * A {@link Template} with no arguments. + */ record ZeroArgs(Supplier function) implements Template { TemplateBody instantiate() { return function.get(); } + /** + * Creates a {@link TemplateWithArgs} which can be used as a {@link Token} inside + * a {@link Template} for nested code generation, and it can also be used with + * {@link TemplateWithArgs#render} to render the template to a {@link String} + * directly. + */ public TemplateWithArgs.ZeroArgsUse withArgs() { return new TemplateWithArgs.ZeroArgsUse(this); } } + + /** + * A {@link Template} with one argument. + */ record OneArgs(String arg0Name, Function function) implements Template { TemplateBody instantiate(A a) { return function.apply(a); @@ -60,6 +103,9 @@ public TemplateWithArgs.OneArgsUse withArgs(A a) { } } + /** + * A {@link Template} with two arguments. + */ record TwoArgs(String arg0Name, String arg1Name, BiFunction function) implements Template { TemplateBody instantiate(A a, B b) { @@ -71,23 +117,101 @@ public TemplateWithArgs.TwoArgsUse withArgs(A a, B b) { } } + /** + * Creates a {@link TemplateBody} from a list of tokens, which can be {@link String}s, + * boxed primitive types (e.g. {@link Integer}), any {@link Token}, or {@link List}s + * of any of these. + * + * @param tokens A list of tokens, which can be {@link String}s,boxed primitive types + * (e.g. {@link Integer}), any {@link Token}, or {@link List}s + * of any of these. + * @return The {@link TemplateBody} which captures the list of validated tokens. + * @throws IllegalArgumentException if the list of tokens contains an unexpected object. + */ static TemplateBody body(Object... tokens) { return new TemplateBody(Token.parse(tokens)); } - static HookIntoToken intoHook(Hook hook, TemplateWithArgs t) { - return new HookIntoToken(hook, t); - } - + /** + * Let a {@link TemplateWithArgs} generate code at the innermost location where the + * {@link Hook} was set with {@link Hook#set}. + * + * @param hook The {@link Hook} the code is to be generated at. + * @param templateWithArgs The {@link Template} with applied arguments to be generated at the {@link Hook}. + * @return The {@link HookIntoToken} which when used inside a {@link Template#body} performs the code generation into the {@link Hook}. + * @throws RendererException if there is no active {@link Hook#set}. + */ + static HookIntoToken intoHook(Hook hook, TemplateWithArgs templateWithArgs) { + return new HookIntoToken(hook, templateWithArgs); + } + + /** + * Retrieves the renaming (dollar-replacement) of the {@code 'name'} for the + * current {@link Template} that is being instanciated. It returns the same + * dollar-replacement as the string use {@code "$name"}. + * + * Here an example where a {@link Template} creates a local variable {@code 'var'}, + * with an implicit dollar-replacement, and then captures that dollar-replacement + * using {@link $} for the use inside a nested template. + * {@snippet lang=java : + * var template = Template.make(() -> body( + * """ + * int $var = 42; + * """, + * otherTemplate.withArgs($("var")) + * )); + * } + * + * @param name The {@link String} name of the name. + * @return The dollar-replacement for the {@code 'name'}. + */ static String $(String name) { return Renderer.getCurrent().$(name); } + /** + * Define a hashtag replacement for {@code "#key"}, with a specific value. + * + * {@snippet lang=java : + * var template = Template.make("a", (Integer a) -> body( + * let("b", a * 5), + * """ + * System.out.prinln("Use a and b with hashtag replacement: #a and #b"); + * """ + * )); + * } + * + * @param key Name for the hashtag replacement. + * @param value The value that the hashtag is replaced with. + * @return A token that does nothing, so that the {@link let} cal can easily be put in a list of tokens + * inside a {@link Template#body}. + * @throws RendererException if there is a duplicate hashtag {@code key}. + */ static NothingToken let(String key, Object value) { Renderer.getCurrent().addHashtagReplacement(key, value); return new NothingToken(); } + /** + * Define a hashtag replacement for {@code "#key"}, with a specific value, which is also captured + * by the provided {@code 'function'} with type {@code }. + * + * {@snippet lang=java : + * var template = Template.make("a", (Integer a) -> let("b", a * 2, (Integer b) -> body( + * """ + * System.out.prinln("Use a and b with hashtag replacement: #a and #b"); + * """, + * "System.out.println(\"Use a and b as capture variables:\" + a + " and " + b + ");\n" + * ))); + * } + * + * @param key Name for the hashtag replacement. + * @param value The value that the hashtag is replaced with. + * @param function The function that is applied with the provided {@code 'value'}. + * @return A token that does nothing, so that the {@link let} cal can easily be put in a list of tokens + * inside a {@link Template#body}. + * @throws RendererException if there is a duplicate hashtag {@code key}. + */ static TemplateBody let(String key, T value, Function function) { Renderer.getCurrent().addHashtagReplacement(key, value); return function.apply(value); From ee35c31f718c9c574b86cb58f578b05cc8557f84 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 17 Feb 2025 14:57:03 +0100 Subject: [PATCH 083/212] more documentation --- .../lib/template_framework/Renderer.java | 2 +- .../lib/template_framework/Template.java | 129 ++++++++++++++++++ .../lib/template_framework/TemplateFrame.java | 5 +- 3 files changed, 131 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 7b4a4011ff4f6..68814b4f9d84c 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -68,7 +68,7 @@ static Renderer getCurrent() { } static String render(TemplateWithArgs templateWithArgs) { - return render(templateWithArgs, TemplateFrame.DEFAULT_FUEL); + return render(templateWithArgs, Template.DEFAULT_FUEL); } static String render(TemplateWithArgs templateWithArgs, float fuel) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 8cbd01e4c29fd..6ac2ea61a05f7 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -36,6 +36,17 @@ public interface Template { /** * Creates a {@link Template} with no arguments. + * See {@link body} for more details about how to construct a {@link Template} with {@link Token}s. + * + *

+ * Example: + * {@snippet lang=java : + * var template = Template.make(() -> body( + * """ + * Multi-liine string or other tokens. + * """ + * )); + * } * * @param body The {@link TemplateBody} created by {@link Template#body}. * @return A {@link Template} with zero arguments. @@ -46,6 +57,21 @@ static ZeroArgs make(Supplier body) { /** * Creates a {@link Template} with one argument. + * See {@link body} for more details about how to construct a {@link Template} with {@link Token}s. + * + *

+ * Here an example with template argument {@code 'a'}, captured once as string name + * for use in hashtag replacements, and captured once as lambda argument with the corresponding type + * of the generic argument. + * {@snippet lang=java : + * var template = Template.make("a", (Integer a) -> body( + * """ + * Multi-liine string or other tokens. + * We can use the hashtag replacement #a to directly insert the String value of a. + * """, + * "We can also use the captured parameter of a: " + a + * )); + * } * * @param body The {@link TemplateBody} created by {@link Template#body}. * @param Type of the zeroth argument. @@ -58,6 +84,21 @@ static OneArgs make(String arg0Name, Function body) { /** * Creates a {@link Template} with two arguments. + * See {@link body} for more details about how to construct a {@link Template} with {@link Token}s. + * + *

+ * Here an example with template arguments {@code 'a'} and {@code 'b'}, captured once as string names + * for use in hashtag replacements, and captured once as lambda arguments with the corresponding types + * of the generic arguments. + * {@snippet lang=java : + * var template = Template.make("a", "b", (Integer a, String b) -> body( + * """ + * Multi-liine string or other tokens. + * We can use the hashtag replacement #a and #b to directly insert the String value of a and b. + * """, + * "We can also use the captured parameter of a and b: " + a + " and " + b + * )); + * } * * @param body The {@link TemplateBody} created by {@link Template#body}. * @param Type of the zeroth argument. @@ -122,6 +163,17 @@ public TemplateWithArgs.TwoArgsUse withArgs(A a, B b) { * boxed primitive types (e.g. {@link Integer}), any {@link Token}, or {@link List}s * of any of these. * + * {@snippet lang=java : + * var template = Template.make(() -> body( + * """ + * Multi-line string + * """, + * "normal string ", Integer.valueOf(3), Float.valueOf(1.5f), + * List.of("abc", "def"), + * nestedTemplate.withArgs(42) + * )); + * } + * * @param tokens A list of tokens, which can be {@link String}s,boxed primitive types * (e.g. {@link Integer}), any {@link Token}, or {@link List}s * of any of these. @@ -136,6 +188,38 @@ static TemplateBody body(Object... tokens) { * Let a {@link TemplateWithArgs} generate code at the innermost location where the * {@link Hook} was set with {@link Hook#set}. * + * Example: + * {@snippet lang=java : + * var myHook = new Hook("MyHook"); + * + * var template1 = Template.make("name", (String name) -> body( + * """ + * public static int #name = 42; + * """ + * )); + * + * var template2 = Template.make(() -> body( + * """ + * public class Test { + * """, + * // Set the hook here. + * myHook.set( + * """ + * public static void main(String[] args) { + * System.out.println("$field: " + $field) + * """, + * // Reach up to where the hook was set, and insert the code of template1. + * intoHook(template1.withArgs($("field"))), + * """ + * } + * """ + * ), + * """ + * } + * """ + * )); + * } + * * @param hook The {@link Hook} the code is to be generated at. * @param templateWithArgs The {@link Template} with applied arguments to be generated at the {@link Hook}. * @return The {@link HookIntoToken} which when used inside a {@link Template#body} performs the code generation into the {@link Hook}. @@ -217,10 +301,55 @@ static TemplateBody let(String key, T value, Function funct return function.apply(value); } + /** + * Default amount of fuel for {@link TemplateWithArgs#render}. It guides the nesting depth of {@link Template}s. + */ + public final static float DEFAULT_FUEL = 100.0f; + + /** + * The default amount of fuel spent per {@link Template}. It is suptracted from the current {@link fuel} at every + * nesting level, and once the {@link fuel} reaches zero, the nesting is supposed to terminate. + */ + public final static float DEFAULT_FUEL_COST = 10.0f; + + /** + * The current remaining fuel for nested {@link Template}s. Every level of {@link Template} nestig + * subtracts a certain amount of fuel, and when it reaches zero, {@link Template}s are supposed to + * stop nesting, if possible. This is not a hard rule, but a guide, and a mechanism to ensure + * termination in recursive {@link Template} instantiations. + * + *

+ * Example of a recursive {@link Template}, which checks the remaining {@link fuel} at every level, + * and terminates if it reaches zero. It also demonstrates the use of {@link TemplateBinding} for + * the recursive use of {@link Template}s. We {@link TemplateWithArgs#render} with {@code 30} total fuel, and spending {@code 5} fuel at each recursion level. + * {@snippet lang=java : + * var binding = new TemplateBinding>(); + * var template = Template.make("depth", (Integer depth) -> body( + * setFuelCost(5.0f), + * let("fuel", fuel()), + * """ + * System.out.println("Currently at depth #depth with fuel #fuel"); + * """ + * (fuel() > 0) ? binding.get().withArgs(depth + 1) + * "// terminate\n" + * )); + * binding.bind(template); + * String code = template.withArgs(0).render(30.0f); + * } + * + * @return The amount of fuel left for nested {@link Template} use. + */ static float fuel() { return Renderer.getCurrent().fuel(); } + /** + * Changes the amount of fuel used for the current {@link Template}, where the default is + * {@link Template#DEFAULT_FUEL_COST}. + * + * @param fuelCost The amount of fuel used for the current {@link Template}. + * @return A token for convenient use in {@link Template#body}. + */ static NothingToken setFuelCost(float fuelCost) { Renderer.getCurrent().setFuelCost(fuelCost); return new NothingToken(); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java index 6ca7838a3a04b..fee81ea39fb1f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java @@ -37,9 +37,6 @@ * termination criterion to avoid nesting templates too deeply. */ class TemplateFrame { - final static float DEFAULT_FUEL = 100.0f; - final static float DEFAULT_FUEL_COST = 10.0f; - final TemplateFrame parent; final int id; final Map hashtagReplacements = new HashMap<>(); @@ -51,7 +48,7 @@ public static TemplateFrame makeBase(int id, float fuel) { } public static TemplateFrame make(TemplateFrame parent, int id) { - return new TemplateFrame(parent, id, parent.fuel - parent.fuelCost, DEFAULT_FUEL_COST); + return new TemplateFrame(parent, id, parent.fuel - parent.fuelCost, Template.DEFAULT_FUEL_COST); } private TemplateFrame(TemplateFrame parent, int id, float fuel, float fuelCost) { From c880eff33b8537c46a669b8af4d6b7a825202f43 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 17 Feb 2025 15:41:11 +0100 Subject: [PATCH 084/212] use token for defineName --- .../template_framework/DefineNameToken.java | 26 +++++++++++++ .../compiler/lib/template_framework/Hook.java | 2 +- .../lib/template_framework/HookIntoToken.java | 2 +- .../lib/template_framework/HookSetToken.java | 2 +- .../lib/template_framework/Renderer.java | 7 ++-- .../lib/template_framework/Template.java | 38 +++++++++++++++---- .../lib/template_framework/Token.java | 1 + .../tests/TestTemplate.java | 9 ++--- 8 files changed, 68 insertions(+), 19 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/DefineNameToken.java diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/DefineNameToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/DefineNameToken.java new file mode 100644 index 0000000000000..ed8cfa8918c7d --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/DefineNameToken.java @@ -0,0 +1,26 @@ +/* + * 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; + +record DefineNameToken(String name, Object type, NameSelection nameSelection) implements Token {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java index e8a604c6add17..a35f2f3e51df2 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java @@ -26,7 +26,7 @@ import java.util.List; public record Hook(String name) { - public HookSetToken set(Object... tokens) { + public Token set(Object... tokens) { return new HookSetToken(this, Token.parse(tokens)); } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/HookIntoToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/HookIntoToken.java index 73ecbf508c638..411c70e6d3fe1 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/HookIntoToken.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/HookIntoToken.java @@ -23,4 +23,4 @@ package compiler.lib.template_framework; -public record HookIntoToken(Hook hook, TemplateWithArgs templateWithArgs) implements Token {} +record HookIntoToken(Hook hook, TemplateWithArgs templateWithArgs) implements Token {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/HookSetToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/HookSetToken.java index f20cb3214c9ab..40b7304ce8d96 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/HookSetToken.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/HookSetToken.java @@ -25,4 +25,4 @@ import java.util.List; -public record HookSetToken(Hook hook, List tokens) implements Token {} +record HookSetToken(Hook hook, List tokens) implements Token {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 68814b4f9d84c..e947ef1351355 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -124,10 +124,6 @@ void setFuelCost(float fuelCost) { currentTemplateFrame.setFuelCost(fuelCost); } - void defineName(String name, Object type, NameSelection nameSelection) { - currentCodeFrame.defineName(name, type, nameSelection); - } - int countNames(Object type, NameSelection nameSelection) { return currentCodeFrame.countNames(type, nameSelection); } @@ -253,6 +249,9 @@ case HookIntoToken(Hook hook, TemplateWithArgs t) -> { callerCodeFrame.addCode(currentCodeFrame.getCode()); currentCodeFrame = callerCodeFrame; } + case DefineNameToken(String name, Object type, NameSelection nameSelection) -> { + currentCodeFrame.defineName(name, type, nameSelection); + } } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 6ac2ea61a05f7..a4dd518c2f978 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -222,10 +222,10 @@ static TemplateBody body(Object... tokens) { * * @param hook The {@link Hook} the code is to be generated at. * @param templateWithArgs The {@link Template} with applied arguments to be generated at the {@link Hook}. - * @return The {@link HookIntoToken} which when used inside a {@link Template#body} performs the code generation into the {@link Hook}. + * @return The {@link Token} which when used inside a {@link Template#body} performs the code generation into the {@link Hook}. * @throws RendererException if there is no active {@link Hook#set}. */ - static HookIntoToken intoHook(Hook hook, TemplateWithArgs templateWithArgs) { + static Token intoHook(Hook hook, TemplateWithArgs templateWithArgs) { return new HookIntoToken(hook, templateWithArgs); } @@ -271,7 +271,7 @@ static HookIntoToken intoHook(Hook hook, TemplateWithArgs templateWithArgs) { * inside a {@link Template#body}. * @throws RendererException if there is a duplicate hashtag {@code key}. */ - static NothingToken let(String key, Object value) { + static Token let(String key, Object value) { Renderer.getCurrent().addHashtagReplacement(key, value); return new NothingToken(); } @@ -350,20 +350,44 @@ static float fuel() { * @param fuelCost The amount of fuel used for the current {@link Template}. * @return A token for convenient use in {@link Template#body}. */ - static NothingToken setFuelCost(float fuelCost) { + static Token setFuelCost(float fuelCost) { Renderer.getCurrent().setFuelCost(fuelCost); return new NothingToken(); } - static NothingToken defineName(String name, Object type, NameSelection nameSelection) { - Renderer.getCurrent().defineName(name, type, nameSelection); - return new NothingToken(); + /** + * Define a name in the current code frame. + * Note that there can be duplicate definitions, and they simply increase + * the {@link countNames} count, and increase the probability of sampling + * the name with {@link sampleName}. + * + * @param name The {@code 'name'} of the name. + * @param type The type of the name. + * @param nameSelection Determines if the name is mutable or immutable. + * @return The token that performs the defining action. + */ + static Token defineName(String name, Object type, NameSelection nameSelection) { + return new DefineNameToken(name, type, nameSelection); } + /** + * Count the number of names defined for the specified type. + * + * @param type The type of the names to be counted. + * @param nameSelection Determines if we count the mutable names or all. + * @return The number of names for the specified parameters. + */ static int countNames(Object type, NameSelection nameSelection) { return Renderer.getCurrent().countNames(type, nameSelection); } + /** + * Sample a random name for the specified type. + * + * @param type The type of the names to sample from. + * @param nameSelection Determines if we sample from the mutable names or all. + * @return The sampled name. + */ static String sampleName(Object type, NameSelection nameSelection) { return Renderer.getCurrent().sampleName(type, nameSelection); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java index b7b6a8b4e6645..efec7e835b3d2 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java @@ -34,6 +34,7 @@ sealed interface Token permits StringToken, TemplateWithArgs.TwoArgsUse, HookSetToken, HookIntoToken, + DefineNameToken, NothingToken { static List parse(Object[] objects) { diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index ad3d33d4efb35..7e3c47032bd4d 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -85,7 +85,6 @@ public static void main(String[] args) { expectRendererException(() -> let("x","y"), "A Template method such as"); expectRendererException(() -> fuel(), "A Template method such as"); expectRendererException(() -> setFuelCost(1.0f), "A Template method such as"); - expectRendererException(() -> defineName("name", "int", MUTABLE), "A Template method such as"); expectRendererException(() -> countNames("int", MUTABLE), "A Template method such as"); expectRendererException(() -> sampleName("int", MUTABLE), "A Template method such as"); expectRendererException(() -> testFailingHook(), "Hook 'Hook1' was referenced but not found!"); @@ -1002,14 +1001,14 @@ public static void testNames3() { String expected = """ class Y_1 { - [int: 1 and 2] + [int: 0 and 0] begin body_1 - [int: 1 and 2] + [int: 0 and 0] define mutable - [int: 1 and 2] + [int: 1 and 1] define immutable [int: 1 and 2] - [int: 1 and 2] + [int: 0 and 0] } """; checkEQ(code, expected); From 9b64bd56fef89675386999ba072e6ee1aeec75f0 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 17 Feb 2025 15:48:17 +0100 Subject: [PATCH 085/212] more documentation --- .../lib/template_framework/Template.java | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index a4dd518c2f978..08f6c5ee94e06 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -124,8 +124,10 @@ TemplateBody instantiate() { * a {@link Template} for nested code generation, and it can also be used with * {@link TemplateWithArgs#render} to render the template to a {@link String} * directly. + * + * @return The template all (zero) arguments applied. */ - public TemplateWithArgs.ZeroArgsUse withArgs() { + public TemplateWithArgs withArgs() { return new TemplateWithArgs.ZeroArgsUse(this); } } @@ -139,7 +141,16 @@ TemplateBody instantiate(A a) { return function.apply(a); } - public TemplateWithArgs.OneArgsUse withArgs(A a) { + /** + * Creates a {@link TemplateWithArgs} which can be used as a {@link Token} inside + * a {@link Template} for nested code generation, and it can also be used with + * {@link TemplateWithArgs#render} to render the template to a {@link String} + * directly. + * + * @param a The value for the zeroth argument. + * @return The template its argument applied. + */ + public TemplateWithArgs withArgs(A a) { return new TemplateWithArgs.OneArgsUse<>(this, a); } } @@ -153,7 +164,17 @@ TemplateBody instantiate(A a, B b) { return function.apply(a, b); } - public TemplateWithArgs.TwoArgsUse withArgs(A a, B b) { + /** + * Creates a {@link TemplateWithArgs} which can be used as a {@link Token} inside + * a {@link Template} for nested code generation, and it can also be used with + * {@link TemplateWithArgs#render} to render the template to a {@link String} + * directly. + * + * @param a The value for the zeroth argument. + * @param b The value for the first argument. + * @return The template all (two) arguments applied. + */ + public TemplateWithArgs withArgs(A a, B b) { return new TemplateWithArgs.TwoArgsUse<>(this, a, b); } } @@ -291,6 +312,7 @@ static Token let(String key, Object value) { * * @param key Name for the hashtag replacement. * @param value The value that the hashtag is replaced with. + * @param The type of the value. * @param function The function that is applied with the provided {@code 'value'}. * @return A token that does nothing, so that the {@link let} cal can easily be put in a list of tokens * inside a {@link Template#body}. From 1a01a33aac9f43bb7a130699f6bcf0ab0fdff11b Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 17 Feb 2025 15:57:10 +0100 Subject: [PATCH 086/212] more documentation --- .../compiler/lib/template_framework/Renderer.java | 12 ++++++++++-- .../lib/template_framework/RendererException.java | 6 +++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index e947ef1351355..adbb7c91be2bf 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -33,7 +33,15 @@ import jdk.test.lib.Utils; -public class Renderer { +/** + * The {@link Renderer} class is used to keep track of the states during a nested + * {@link Template} rendering. There can only be a single {@link Renderer} active + * at any point, since there are static methods that reference {@link Renderer#getCurrent}. + * + * The {@link Renderer} instance keeps track of the current frames, + * see {@link TemplateFrame} and {@link CodeFrame}. + */ +class Renderer { private static final Pattern DOLLAR_NAME_PATTERN = Pattern.compile("\\$([a-zA-Z_][a-zA-Z0-9_]*)"); private static final Pattern HASHTAG_REPLACEMENT_PATTERN = Pattern.compile("#([a-zA-Z_][a-zA-Z0-9_]*)"); @@ -51,7 +59,7 @@ public class Renderer { private CodeFrame baseCodeFrame; private CodeFrame currentCodeFrame; - // We do not want any other instances. + // We do not want any other instances, so we keep it private. private Renderer(float fuel) { nextTemplateFrameId = 0; baseTemplateFrame = TemplateFrame.makeBase(nextTemplateFrameId++, fuel); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/RendererException.java b/test/hotspot/jtreg/compiler/lib/template_framework/RendererException.java index 4ec1c8f4da979..2d6d9f40165b8 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/RendererException.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/RendererException.java @@ -23,8 +23,12 @@ package compiler.lib.template_framework; +/** + * This exception is thrown when something goes wrong during {@link Template} + * rendering, or in the use of any of its static methods. + */ public class RendererException extends RuntimeException { - public RendererException(String message) { + RendererException(String message) { super(message); } } From 03d67f21b7b51c764b9c9964cd40c9670c21ef19 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 17 Feb 2025 16:43:50 +0100 Subject: [PATCH 087/212] rm NameSelection --- .../lib/template_framework/CodeFrame.java | 16 +++++------ .../template_framework/DefineNameToken.java | 2 +- .../lib/template_framework/Library.java | 6 ++-- .../lib/template_framework/NameSelection.java | 26 ----------------- .../lib/template_framework/Renderer.java | 12 ++++---- .../lib/template_framework/Template.java | 18 ++++++------ .../examples/TestTutorial.java | 12 ++++---- .../tests/TestTemplate.java | 28 +++++++++---------- 8 files changed, 44 insertions(+), 76 deletions(-) delete mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/NameSelection.java diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java index e51bea1562b36..a3e382d5d050d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java @@ -85,16 +85,16 @@ boolean hasHook(Hook hook) { return hookCodeLists.containsKey(hook); } - private NameSet nameSet(NameSelection nameSelection) { - if (nameSelection == NameSelection.MUTABLE) { + private NameSet nameSet(boolean onlyMutable) { + if (onlyMutable) { return mutableNames; } else { return allNames; } } - void defineName(String name, Object type, NameSelection nameSelection) { - if (nameSelection == NameSelection.MUTABLE) { + void defineName(String name, Object type, boolean mutable) { + if (mutable) { mutableNames.add(name, type); allNames.add(name, type); } else { @@ -102,12 +102,12 @@ void defineName(String name, Object type, NameSelection nameSelection) { } } - int countNames(Object type, NameSelection nameSelection) { - return nameSet(nameSelection).count(type); + int countNames(Object type, boolean onlyMutable) { + return nameSet(onlyMutable).count(type); } - String sampleName(Object type, NameSelection nameSelection) { - return nameSet(nameSelection).sample(type); + String sampleName(Object type, boolean onlyMutable) { + return nameSet(onlyMutable).sample(type); } Code getCode() { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/DefineNameToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/DefineNameToken.java index ed8cfa8918c7d..b36f76d4e2eb6 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/DefineNameToken.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/DefineNameToken.java @@ -23,4 +23,4 @@ package compiler.lib.template_framework; -record DefineNameToken(String name, Object type, NameSelection nameSelection) implements Token {} +record DefineNameToken(String name, Object type, boolean mutable) implements Token {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index 9a56977638268..566b7dd5789d6 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -38,8 +38,6 @@ import static compiler.lib.template_framework.Template.setFuelCost; import static compiler.lib.template_framework.Template.countNames; import static compiler.lib.template_framework.Template.sampleName; -import static compiler.lib.template_framework.NameSelection.ALL; -import static compiler.lib.template_framework.NameSelection.MUTABLE; /** * The Library provides a collection of helpful Templates and Hooks. @@ -194,7 +192,7 @@ public enum ExpressionType { public static final Template.OneArgs LOAD_EXPRESSION = Template.make("type", (ExpressionType type) -> { - if (countNames(type, ALL) == 0 || RANDOM.nextInt(5) == 0) { + if (countNames(type, false) == 0 || RANDOM.nextInt(5) == 0) { return body( // TODO defineName !!! GENERATE_EARLIER_VALUE.withArgs(type, $("early")), @@ -202,7 +200,7 @@ public enum ExpressionType { ); } else { return body( - sampleName(type, ALL) + sampleName(type, false) ); } }); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/NameSelection.java b/test/hotspot/jtreg/compiler/lib/template_framework/NameSelection.java deleted file mode 100644 index b85cb965be38a..0000000000000 --- a/test/hotspot/jtreg/compiler/lib/template_framework/NameSelection.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package compiler.lib.template_framework; - -public enum NameSelection { MUTABLE, ALL } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index adbb7c91be2bf..f352b2cba2b50 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -132,12 +132,12 @@ void setFuelCost(float fuelCost) { currentTemplateFrame.setFuelCost(fuelCost); } - int countNames(Object type, NameSelection nameSelection) { - return currentCodeFrame.countNames(type, nameSelection); + int countNames(Object type, boolean onlyMutable) { + return currentCodeFrame.countNames(type, onlyMutable); } - String sampleName(Object type, NameSelection nameSelection) { - return currentCodeFrame.sampleName(type, nameSelection); + String sampleName(Object type, boolean onlyMutable) { + return currentCodeFrame.sampleName(type, onlyMutable); } static String format(Object value) { @@ -257,8 +257,8 @@ case HookIntoToken(Hook hook, TemplateWithArgs t) -> { callerCodeFrame.addCode(currentCodeFrame.getCode()); currentCodeFrame = callerCodeFrame; } - case DefineNameToken(String name, Object type, NameSelection nameSelection) -> { - currentCodeFrame.defineName(name, type, nameSelection); + case DefineNameToken(String name, Object type, boolean mutable) -> { + currentCodeFrame.defineName(name, type, mutable); } } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 08f6c5ee94e06..f5214494ed3d3 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -385,32 +385,32 @@ static Token setFuelCost(float fuelCost) { * * @param name The {@code 'name'} of the name. * @param type The type of the name. - * @param nameSelection Determines if the name is mutable or immutable. + * @param mutable Determines if the name is mutable or immutable. * @return The token that performs the defining action. */ - static Token defineName(String name, Object type, NameSelection nameSelection) { - return new DefineNameToken(name, type, nameSelection); + static Token defineName(String name, Object type, boolean mutable) { + return new DefineNameToken(name, type, mutable); } /** * Count the number of names defined for the specified type. * * @param type The type of the names to be counted. - * @param nameSelection Determines if we count the mutable names or all. + * @param onlyMutable Determines if we count the mutable names or all. * @return The number of names for the specified parameters. */ - static int countNames(Object type, NameSelection nameSelection) { - return Renderer.getCurrent().countNames(type, nameSelection); + static int countNames(Object type, boolean onlyMutable) { + return Renderer.getCurrent().countNames(type, onlyMutable); } /** * Sample a random name for the specified type. * * @param type The type of the names to sample from. - * @param nameSelection Determines if we sample from the mutable names or all. + * @param onlyMutable Determines if we sample from the mutable names or all. * @return The sampled name. */ - static String sampleName(Object type, NameSelection nameSelection) { - return Renderer.getCurrent().sampleName(type, nameSelection); + static String sampleName(Object type, boolean onlyMutable) { + return Renderer.getCurrent().sampleName(type, onlyMutable); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java index 7dcd8a3fe9f22..50be6383ee250 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java @@ -49,8 +49,6 @@ import static compiler.lib.template_framework.Template.countNames; import static compiler.lib.template_framework.Library.CLASS_HOOK; import static compiler.lib.template_framework.Library.METHOD_HOOK; -import static compiler.lib.template_framework.NameSelection.MUTABLE; -import static compiler.lib.template_framework.NameSelection.ALL; public class TestTutorial { @@ -408,29 +406,29 @@ public static void main() { // These can be used to define and sample variables from outer scopes. public static String generateWithNames() { var templateSample = Template.make("type", (Object type) -> body( - let("name", sampleName(type, ALL)), + let("name", sampleName(type, false)), """ System.out.println("Sampling type #type: #name = " + #name); """ )); var templateStaticField = Template.make("type", (Object type) -> body( - defineName($("field"), type, MUTABLE), + defineName($("field"), type, true), """ public static #type $field = 0; """ )); var templateLocalVariable = Template.make("type", (Object type) -> body( - defineName($("var"), type, MUTABLE), + defineName($("var"), type, true), """ #type $var = 0; """ )); var templateStatus = Template.make(() -> body( - let("ints", countNames("int", ALL)), - let("longs", countNames("long", ALL)), + let("ints", countNames("int", false)), + let("longs", countNames("long", false)), """ System.out.println("Status: #ints ints, #longs longs."); """ diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 7e3c47032bd4d..bffb4c9196b57 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -50,8 +50,6 @@ import static compiler.lib.template_framework.Template.defineName; import static compiler.lib.template_framework.Template.countNames; import static compiler.lib.template_framework.Template.sampleName; -import static compiler.lib.template_framework.NameSelection.MUTABLE; -import static compiler.lib.template_framework.NameSelection.ALL; public class TestTemplate { interface FailingTest { @@ -85,8 +83,8 @@ public static void main(String[] args) { expectRendererException(() -> let("x","y"), "A Template method such as"); expectRendererException(() -> fuel(), "A Template method such as"); expectRendererException(() -> setFuelCost(1.0f), "A Template method such as"); - expectRendererException(() -> countNames("int", MUTABLE), "A Template method such as"); - expectRendererException(() -> sampleName("int", MUTABLE), "A Template method such as"); + expectRendererException(() -> countNames("int", true), "A Template method such as"); + expectRendererException(() -> sampleName("int", true), "A Template method such as"); expectRendererException(() -> testFailingHook(), "Hook 'Hook1' was referenced but not found!"); expectRendererException(() -> testFailingSample(), "No variable of type 'int'."); expectRendererException(() -> testFailingHashtag1(), "Duplicate hashtag replacement for #a"); @@ -808,11 +806,11 @@ public static void testNames() { var hook1 = new Hook("Hook1"); var template1 = Template.make(() -> body( - "[", countNames("int", MUTABLE), "]\n" + "[", countNames("int", true), "]\n" )); var template2 = Template.make("name", "type", (String name, Object type) -> body( - defineName(name, type, MUTABLE), + defineName(name, type, true), "define #type #name\n", template1.withArgs() )); @@ -871,18 +869,18 @@ public static void testNames2() { var hook1 = new Hook("Hook1"); var template1 = Template.make("type", (Object type) -> body( - "[#type: ", countNames(type, MUTABLE), " and ", countNames(type, ALL), "]\n" + "[#type: ", countNames(type, true), " and ", countNames(type, false), "]\n" )); var template2 = Template.make("name", "type", (String name, Object type) -> body( - defineName(name, type, MUTABLE), + defineName(name, type, true), "define mutable #type #name\n", template1.withArgs(type) )); var template3 = Template.make("name", "type", (String name, Object type) -> body( - defineName(name, type, ALL), + defineName(name, type, false), "define immutable #type #name\n", template1.withArgs(type) )); @@ -902,14 +900,14 @@ public static void testNames2() { )); var template6 = Template.make("type", (Object type) -> body( - let("v", sampleName(type, MUTABLE)), + let("v", sampleName(type, true)), "{ $sample\n", "#v = 7\n", "} $sample\n" )); var template7 = Template.make("type", (Object type) -> body( - let("v", sampleName(type, ALL)), + let("v", sampleName(type, false)), "{ $sample\n", "blackhole(#v)\n", "} $sample\n" @@ -975,7 +973,7 @@ public static void testNames3() { var hook1 = new Hook("Hook1"); var template1 = Template.make("type", (Object type) -> body( - "[#type: ", countNames(type, MUTABLE), " and ", countNames(type, ALL), "]\n" + "[#type: ", countNames(type, true), " and ", countNames(type, false), "]\n" )); // Example that shows that defineName runs before any code gets generated. @@ -987,10 +985,10 @@ public static void testNames3() { "begin $body\n", template1.withArgs("int"), "define mutable\n", - defineName($("v1"), "int", MUTABLE), + defineName($("v1"), "int", true), template1.withArgs("int"), "define immutable\n", - defineName($("v1"), "int", ALL), + defineName($("v1"), "int", false), template1.withArgs("int") ), template1.withArgs("int"), @@ -1090,7 +1088,7 @@ public static void testFailingHook() { public static void testFailingSample() { var template1 = Template.make(() -> body( - let("v", sampleName("int", MUTABLE)), + let("v", sampleName("int", true)), "v is #v\n" )); From fd1d4ed2d7d60ca5409ee83ccf9ecda1e9b7d4e8 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 18 Feb 2025 08:54:43 +0100 Subject: [PATCH 088/212] more docs and hook.insert refactor --- .../compiler/lib/template_framework/Hook.java | 45 +++++++++++ ...ookIntoToken.java => HookInsertToken.java} | 2 +- .../lib/template_framework/Library.java | 9 +-- .../lib/template_framework/Renderer.java | 2 +- .../lib/template_framework/Template.java | 75 ++++++------------- .../template_framework/TemplateBinding.java | 2 + .../lib/template_framework/TemplateBody.java | 2 + .../template_framework/TemplateWithArgs.java | 5 ++ .../lib/template_framework/Token.java | 2 +- .../examples/TestTutorial.java | 21 +++--- .../tests/TestTemplate.java | 51 +++++++------ 11 files changed, 117 insertions(+), 99 deletions(-) rename test/hotspot/jtreg/compiler/lib/template_framework/{HookIntoToken.java => HookInsertToken.java} (92%) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java index a35f2f3e51df2..4b4dfe183aa1a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java @@ -25,8 +25,53 @@ import java.util.List; +/** + * Let a {@link TemplateWithArgs} generate code at the innermost location where the + * {@link Hook} was set with {@link Hook#set}. + * + * Example: + * {@snippet lang=java : + * var myHook = new Hook("MyHook"); + * + * var template1 = Template.make("name", (String name) -> body( + * """ + * public static int #name = 42; + * """ + * )); + * + * var template2 = Template.make(() -> body( + * """ + * public class Test { + * """, + * // Set the hook here. + * myHook.set( + * """ + * public static void main(String[] args) { + * System.out.println("$field: " + $field) + * """, + * // Reach up to where the hook was set, and insert the code of template1. + * myHook.insert(template1.withArgs($("field"))), + * """ + * } + * """ + * ), + * """ + * } + * """ + * )); + * } + * + * @param hook The {@link Hook} the code is to be generated at. + * @param templateWithArgs The {@link Template} with applied arguments to be generated at the {@link Hook}. + * @return The {@link Token} which when used inside a {@link Template#body} performs the code generation into the {@link Hook}. + * @throws RendererException if there is no active {@link Hook#set}. + */ public record Hook(String name) { public Token set(Object... tokens) { return new HookSetToken(this, Token.parse(tokens)); } + + public Token insert(TemplateWithArgs templateWithArgs) { + return new HookInsertToken(this, templateWithArgs); + } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/HookIntoToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/HookInsertToken.java similarity index 92% rename from test/hotspot/jtreg/compiler/lib/template_framework/HookIntoToken.java rename to test/hotspot/jtreg/compiler/lib/template_framework/HookInsertToken.java index 411c70e6d3fe1..9c21ef4a47803 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/HookIntoToken.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/HookInsertToken.java @@ -23,4 +23,4 @@ package compiler.lib.template_framework; -record HookIntoToken(Hook hook, TemplateWithArgs templateWithArgs) implements Token {} +record HookInsertToken(Hook hook, TemplateWithArgs templateWithArgs) implements Token {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java index 566b7dd5789d6..8e2cd3202675a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Library.java @@ -33,7 +33,6 @@ import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; -import static compiler.lib.template_framework.Template.intoHook; import static compiler.lib.template_framework.Template.fuel; import static compiler.lib.template_framework.Template.setFuelCost; import static compiler.lib.template_framework.Template.countNames; @@ -138,7 +137,7 @@ public enum ExpressionType { public static final Template.OneArgs GENERATE_BOOLEAN_USING_BOXING_INLINE = Template.make("name", (String name) -> body( - intoHook(CLASS_HOOK, DEFINE_STATIC_FIELD.withArgs(ExpressionType.BOOLEAN, $("flag"))), + CLASS_HOOK.insert(DEFINE_STATIC_FIELD.withArgs(ExpressionType.BOOLEAN, $("flag"))), """ // #name is constant, but only known after Incremental Boxing Inline (after parsing). Integer $box; @@ -168,7 +167,7 @@ public enum ExpressionType { GENERATE_BOOLEAN_USING_BOXING_INLINE.withArgs(name), GENERATE_BOOLEAN_USING_EMPTY_LOOP.withArgs(name), // This will never constant fold, as it just loads a boolean: - intoHook(CLASS_HOOK, DEFINE_STATIC_FIELD.withArgs(ExpressionType.BOOLEAN, name)) + CLASS_HOOK.insert(DEFINE_STATIC_FIELD.withArgs(ExpressionType.BOOLEAN, name)) )) )); @@ -185,8 +184,8 @@ public enum ExpressionType { public static final Template.TwoArgs GENERATE_EARLIER_VALUE = Template.make("type", "name", (ExpressionType type, String name) -> body( choice(List.of( - intoHook(CLASS_HOOK, DEFINE_STATIC_FIELD.withArgs(type, name)), - intoHook(METHOD_HOOK, GENERATE_EARLILER_VALUE_FROM_BOOLEAN.withArgs(type, name)) + CLASS_HOOK.insert(DEFINE_STATIC_FIELD.withArgs(type, name)), + METHOD_HOOK.insert(GENERATE_EARLILER_VALUE_FROM_BOOLEAN.withArgs(type, name)) )) )); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index f352b2cba2b50..5bb2232692b03 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -227,7 +227,7 @@ case HookSetToken(Hook hook, List tokens) -> { currentCodeFrame.addCode(hookCodeFrame.getCode()); currentCodeFrame.addCode(innerCodeFrame.getCode()); } - case HookIntoToken(Hook hook, TemplateWithArgs t) -> { + case HookInsertToken(Hook hook, TemplateWithArgs t) -> { // Switch to hook CodeFrame. CodeFrame callerCodeFrame = currentCodeFrame; CodeFrame hookCodeFrame = codeFrameForHook(hook); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index f5214494ed3d3..71e844d073057 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -74,8 +74,8 @@ static ZeroArgs make(Supplier body) { * } * * @param body The {@link TemplateBody} created by {@link Template#body}. - * @param Type of the zeroth argument. - * @param arg0Name The name of the zeroth argument for hashtag replacement. + * @param Type of the (first) argument. + * @param arg0Name The name of the (first) argument for hashtag replacement. * @return A {@link Template} with one argument. */ static OneArgs make(String arg0Name, Function body) { @@ -101,10 +101,10 @@ static OneArgs make(String arg0Name, Function body) { * } * * @param body The {@link TemplateBody} created by {@link Template#body}. - * @param Type of the zeroth argument. - * @param arg0Name The name of the zeroth argument for hashtag replacement. - * @param Type of the first argument. - * @param arg1Name The name of the first argument for hashtag replacement. + * @param Type of the first argument. + * @param arg0Name The name of the first argument for hashtag replacement. + * @param Type of the second argument. + * @param arg1Name The name of the second argument for hashtag replacement. * @return A {@link Template} with two arguments. */ static TwoArgs make(String arg0Name, String arg1Name, BiFunction body) { @@ -113,6 +113,8 @@ static TwoArgs make(String arg0Name, String arg1Name, BiFunction function) implements Template { TemplateBody instantiate() { @@ -135,6 +137,10 @@ public TemplateWithArgs withArgs() { /** * A {@link Template} with one argument. + * + * @param arg0Name The name of the (first) argument, used for hashtag replacements in the {@link Template}. + * @param The type of the (first) argument. + * @param function The {@link Function} that creates the {@link TemplateBody} given the template argument. */ record OneArgs(String arg0Name, Function function) implements Template { TemplateBody instantiate(A a) { @@ -147,7 +153,7 @@ TemplateBody instantiate(A a) { * {@link TemplateWithArgs#render} to render the template to a {@link String} * directly. * - * @param a The value for the zeroth argument. + * @param a The value for the (first) argument. * @return The template its argument applied. */ public TemplateWithArgs withArgs(A a) { @@ -157,6 +163,12 @@ public TemplateWithArgs withArgs(A a) { /** * A {@link Template} with two arguments. + * + * @param arg0Name The name of the first argument, used for hashtag replacements in the {@link Template}. + * @param arg1Name The name of the second argument, used for hashtag replacements in the {@link Template}. + * @param The type of the first argument. + * @param The type of the second argument. + * @param function The {@link BiFunction} that creates the {@link TemplateBody} given the template arguments. */ record TwoArgs(String arg0Name, String arg1Name, BiFunction function) implements Template { @@ -170,8 +182,8 @@ TemplateBody instantiate(A a, B b) { * {@link TemplateWithArgs#render} to render the template to a {@link String} * directly. * - * @param a The value for the zeroth argument. - * @param b The value for the first argument. + * @param a The value for the first argument. + * @param b The value for the second argument. * @return The template all (two) arguments applied. */ public TemplateWithArgs withArgs(A a, B b) { @@ -205,51 +217,6 @@ static TemplateBody body(Object... tokens) { return new TemplateBody(Token.parse(tokens)); } - /** - * Let a {@link TemplateWithArgs} generate code at the innermost location where the - * {@link Hook} was set with {@link Hook#set}. - * - * Example: - * {@snippet lang=java : - * var myHook = new Hook("MyHook"); - * - * var template1 = Template.make("name", (String name) -> body( - * """ - * public static int #name = 42; - * """ - * )); - * - * var template2 = Template.make(() -> body( - * """ - * public class Test { - * """, - * // Set the hook here. - * myHook.set( - * """ - * public static void main(String[] args) { - * System.out.println("$field: " + $field) - * """, - * // Reach up to where the hook was set, and insert the code of template1. - * intoHook(template1.withArgs($("field"))), - * """ - * } - * """ - * ), - * """ - * } - * """ - * )); - * } - * - * @param hook The {@link Hook} the code is to be generated at. - * @param templateWithArgs The {@link Template} with applied arguments to be generated at the {@link Hook}. - * @return The {@link Token} which when used inside a {@link Template#body} performs the code generation into the {@link Hook}. - * @throws RendererException if there is no active {@link Hook#set}. - */ - static Token intoHook(Hook hook, TemplateWithArgs templateWithArgs) { - return new HookIntoToken(hook, templateWithArgs); - } - /** * Retrieves the renaming (dollar-replacement) of the {@code 'name'} for the * current {@link Template} that is being instanciated. It returns the same diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBinding.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBinding.java index c077fd76381a0..15f36f91fd961 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBinding.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBinding.java @@ -31,6 +31,8 @@ * it can be used with {@link get} inside a {@link Template}. Later, we can {@link bind} * a {@link Template} to the binding, such that {@link get} returns that bound * {@link Template}. + * + * @param Type of the template. */ public class TemplateBinding { private T template = null; diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBody.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBody.java index b30f10e8ce45d..5e71c7d5b5207 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBody.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBody.java @@ -28,5 +28,7 @@ /** * A {@link Template} generates a {@link TemplateBody}, which is a list of {@link Token}s, * which are then later rendered to {@link String}s. + * + * @param tokens The list of {@link Token}s that are later rendered to {@link String}s. */ public record TemplateBody(List tokens) {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java index 814db65aae9dc..308da9dc63ada 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java @@ -57,6 +57,8 @@ public void visitArguments(ArgumentVisitor visitor) {} /** * Represents a one-argument {@link Template} with applied arguments, ready for instantiation * either as a {@link Token} inside another {@link Template} or with {@link render}. + * + * @param The type of the (first) argument. */ public static final class OneArgsUse extends TemplateWithArgs implements Token { private final Template.OneArgs oneArgs; @@ -81,6 +83,9 @@ public void visitArguments(ArgumentVisitor visitor) { /** * Represents a two-argument {@link Template} with applied arguments, ready for instantiation * either as a {@link Token} inside another {@link Template} or with {@link render}. + * + * @param The type of the first argument. + * @param The type of the second argument. */ public static final class TwoArgsUse extends TemplateWithArgs implements Token { private final Template.TwoArgs twoArgs; diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java index efec7e835b3d2..13bfded18b29f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java @@ -33,7 +33,7 @@ sealed interface Token permits StringToken, TemplateWithArgs.OneArgsUse, TemplateWithArgs.TwoArgsUse, HookSetToken, - HookIntoToken, + HookInsertToken, DefineNameToken, NothingToken { diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java index 50be6383ee250..8bd4583b97c08 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java @@ -42,7 +42,6 @@ import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; -import static compiler.lib.template_framework.Template.intoHook; import static compiler.lib.template_framework.Template.fuel; import static compiler.lib.template_framework.Template.defineName; import static compiler.lib.template_framework.Template.sampleName; @@ -241,7 +240,7 @@ public static String generateWithCustomHooks() { """ // Let us go back to the hook, and define a field named $field... """, - intoHook(myHook, template1.withArgs($("field"), x)), + myHook.insert(template1.withArgs($("field"), x)), """ System.out.println("$field: " + $field); if ($field != #x) { throw new RuntimeException("Wrong value!"); } @@ -256,7 +255,7 @@ public class InnerTest4 { """, // We set a Hook outside the main method, but inside the Class. // The Hook is set for the Tokens inside the set braces. - // As long as the hook is set, we can generate code into the hook, + // As long as the hook is set, we can insert code into the hook, // here we can define static fields for example. myHook.set( """ @@ -298,8 +297,8 @@ public static String generateWithLibraryHooks() { """ // Let's define a local variable $var and a static field $field. """, - intoHook(CLASS_HOOK, templateStaticField.withArgs($("field"), 5)), - intoHook(METHOD_HOOK, templateLocalVariable.withArgs($("var"), 11)), + CLASS_HOOK.insert(templateStaticField.withArgs($("field"), 5)), + METHOD_HOOK.insert(templateLocalVariable.withArgs($("var"), 11)), """ System.out.println("$field: " + $field); System.out.println("$var: " + $var); @@ -335,7 +334,7 @@ public static void main() { private static void other() { """, - // Have a separate method hook for other, so that it can generate + // Have a separate method hook for other, so that it can insert // its own local variables. METHOD_HOOK.set( """ @@ -439,10 +438,10 @@ public static String generateWithNames() { System.out.println("Starting inside main..."); """, templateStatus.withArgs(), - intoHook(METHOD_HOOK, templateLocalVariable.withArgs("int")), - intoHook(METHOD_HOOK, templateLocalVariable.withArgs("long")), - intoHook(CLASS_HOOK, templateStaticField.withArgs("int")), - intoHook(CLASS_HOOK, templateStaticField.withArgs("long")), + METHOD_HOOK.insert(templateLocalVariable.withArgs("int")), + METHOD_HOOK.insert(templateLocalVariable.withArgs("long")), + CLASS_HOOK.insert(templateStaticField.withArgs("int")), + CLASS_HOOK.insert(templateStaticField.withArgs("long")), templateStatus.withArgs(), // We should see a mix if fields and variables sampled. Collections.nCopies(5, templateSample.withArgs("int")), @@ -495,7 +494,7 @@ public static void main() { private static void other() { """, - // Have a separate method hook for other, so that it can generate + // Have a separate method hook for other, so that it can insert // its own local variables. METHOD_HOOK.set( """ diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index bffb4c9196b57..c70a10b63dbbc 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -42,7 +42,6 @@ import compiler.lib.template_framework.TemplateBinding; import compiler.lib.template_framework.RendererException; import static compiler.lib.template_framework.Template.body; -import static compiler.lib.template_framework.Template.intoHook; import static compiler.lib.template_framework.Template.$; import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.fuel; @@ -243,7 +242,7 @@ public static void testHookSimple() { "{\n", hook1.set( "World\n", - intoHook(hook1, template1.withArgs()) + hook1.insert(template1.withArgs()) ), "}" )); @@ -271,22 +270,22 @@ public static void testHookNested() { hook1.set( template1.withArgs("one"), template1.withArgs("two"), - intoHook(hook1, template1.withArgs("intoHook1a")), - intoHook(hook1, template1.withArgs("intoHook1b")), + hook1.insert(template1.withArgs("intoHook1a")), + hook1.insert(template1.withArgs("intoHook1b")), template1.withArgs("three"), hook1.set( template1.withArgs("four"), - intoHook(hook1, template1.withArgs("intoHook1c")), + hook1.insert(template1.withArgs("intoHook1c")), template1.withArgs("five") ), template1.withArgs("six"), hook1.set(), // empty template1.withArgs("seven"), - intoHook(hook1, template1.withArgs("intoHook1d")), + hook1.insert(template1.withArgs("intoHook1d")), template1.withArgs("eight"), hook1.set( template1.withArgs("nine"), - intoHook(hook1, template1.withArgs("intoHook1e")), + hook1.insert(template1.withArgs("intoHook1e")), template1.withArgs("ten") ), template1.withArgs("eleven") @@ -328,28 +327,28 @@ public static void testHookWithNestedTemplates() { var template2 = Template.make("b", (String b) -> body( "{\n", template1.withArgs(b + "A"), - intoHook(hook1, template1.withArgs(b + "B")), - intoHook(hook2, template1.withArgs(b + "C")), + hook1.insert(template1.withArgs(b + "B")), + hook2.insert(template1.withArgs(b + "C")), template1.withArgs(b + "D"), hook1.set( template1.withArgs(b + "E"), - intoHook(hook1, template1.withArgs(b + "F")), - intoHook(hook2, template1.withArgs(b + "G")), + hook1.insert(template1.withArgs(b + "F")), + hook2.insert(template1.withArgs(b + "G")), template1.withArgs(b + "H"), hook2.set( template1.withArgs(b + "I"), - intoHook(hook1, template1.withArgs(b + "J")), - intoHook(hook2, template1.withArgs(b + "K")), + hook1.insert(template1.withArgs(b + "J")), + hook2.insert(template1.withArgs(b + "K")), template1.withArgs(b + "L") ), template1.withArgs(b + "M"), - intoHook(hook1, template1.withArgs(b + "N")), - intoHook(hook2, template1.withArgs(b + "O")), + hook1.insert(template1.withArgs(b + "N")), + hook2.insert(template1.withArgs(b + "O")), template1.withArgs(b + "O") ), template1.withArgs(b + "P"), - intoHook(hook1, template1.withArgs(b + "Q")), - intoHook(hook2, template1.withArgs(b + "R")), + hook1.insert(template1.withArgs(b + "Q")), + hook2.insert(template1.withArgs(b + "R")), template1.withArgs(b + "S"), "}\n" )); @@ -416,13 +415,13 @@ public static void testHookRecursion() { var template2 = Template.make("b", (String b) -> body( "<\n", template1.withArgs(b + "A"), - intoHook(hook1, template1.withArgs(b + "B")), // sub-B is rendered before template2. + hook1.insert(template1.withArgs(b + "B")), // sub-B is rendered before template2. template1.withArgs(b + "C"), "inner-hook-start\n", hook1.set( "inner-hook-end\n", template1.withArgs(b + "E"), - intoHook(hook1, template1.withArgs(b + "E")), + hook1.insert(template1.withArgs(b + "E")), template1.withArgs(b + "F") ), ">\n" @@ -434,7 +433,7 @@ public static void testHookRecursion() { "hook-start\n", hook1.set( "hook-end\n", - intoHook(hook1, template2.withArgs("sub-")), + hook1.insert(template2.withArgs("sub-")), "base-C\n" ), "base-D\n", @@ -491,11 +490,11 @@ public static void testDollar() { "break\n", hook1.set( "one\n", - intoHook(hook1, template1.withArgs($("name"))), + hook1.insert(template1.withArgs($("name"))), "two\n", template1.withArgs($("name")), "three\n", - intoHook(hook1, template2.withArgs($("name"))), + hook1.insert(template2.withArgs($("name"))), "four\n" ), "}\n" @@ -817,7 +816,7 @@ public static void testNames() { var template3 = Template.make(() -> body( "<\n", - intoHook(hook1, template2.withArgs($("name"), "int")), + hook1.insert(template2.withArgs($("name"), "int")), "$name = 5\n", ">\n" )); @@ -887,14 +886,14 @@ public static void testNames2() { var template4 = Template.make("type", (Object type) -> body( "{ $store\n", - intoHook(hook1, template2.withArgs($("name"), type)), + hook1.insert(template2.withArgs($("name"), type)), "$name = 5\n", "} $store\n" )); var template5 = Template.make("type", (Object type) -> body( "{ $load\n", - intoHook(hook1, template3.withArgs($("name"), type)), + hook1.insert(template3.withArgs($("name"), type)), "blackhole($name)\n", "} $load\n" )); @@ -1079,7 +1078,7 @@ public static void testFailingHook() { var template2 = Template.make(() -> body( "beta\n", // Use hook without hook1.set - intoHook(hook1, template1.withArgs()), + hook1.insert(template1.withArgs()), "gamma\n" )); From 2d70ba3eac25762afe04e7ce6773973e6b9ed07b Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 18 Feb 2025 09:48:07 +0100 Subject: [PATCH 089/212] hook documentation --- .../compiler/lib/template_framework/Hook.java | 29 ++++++++++++++----- .../lib/template_framework/Template.java | 2 +- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java index 4b4dfe183aa1a..2863bcf277a8f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java @@ -26,8 +26,11 @@ import java.util.List; /** - * Let a {@link TemplateWithArgs} generate code at the innermost location where the - * {@link Hook} was set with {@link Hook#set}. + * {@link Hook}s can be {@link set} for a certain scope in a {@link Template}, and all nested + * {@link Template}s in this scope, and then from within this scope, any {@link Template} can + * {@link insert} code to where the {@link Hook} was {@link set}. This can be useful to reach + * "back" or to some outer scope, e.g. while generating code for a method, one can reach out + * to the class scope to insert fields. * * Example: * {@snippet lang=java : @@ -49,7 +52,7 @@ * public static void main(String[] args) { * System.out.println("$field: " + $field) * """, - * // Reach up to where the hook was set, and insert the code of template1. + * // Reach out to where the hook was set, and insert the code of template1. * myHook.insert(template1.withArgs($("field"))), * """ * } @@ -61,16 +64,28 @@ * )); * } * - * @param hook The {@link Hook} the code is to be generated at. - * @param templateWithArgs The {@link Template} with applied arguments to be generated at the {@link Hook}. - * @return The {@link Token} which when used inside a {@link Template#body} performs the code generation into the {@link Hook}. - * @throws RendererException if there is no active {@link Hook#set}. + * @param name The name of the Hook, for debugging purposes only. */ public record Hook(String name) { + /** + * Set this {@link Hook} for the scope of the provided {@code 'tokens'}. + * From anywhere inside this scope, even in nested {@link Template}s, code can be + * {@link insert}ed back to the location where this {@link Hook} was {@link set}. + * + * @param tokens A list of tokens, which have the same restrictions as {@link Template#body}. + * @return A {@link Token} that captures the setting of the scope and the list of validated {@link Token}s. + */ public Token set(Object... tokens) { return new HookSetToken(this, Token.parse(tokens)); } + /** + * Inserts a {@link TemplateWithArgs} to the innermost location where this {@link Hook} was {@link set}. + * This could be in the same {@link Template}, or one nested further out. + * + * @param templateWithArgs The {@link Template} with applied arguments to be inserted at the {@link Hook}. + * @return The {@link Token} which when used inside a {@link Template#body} performs the code insertion into the {@link Hook}. + */ public Token insert(TemplateWithArgs templateWithArgs) { return new HookInsertToken(this, templateWithArgs); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 71e844d073057..166964ecf73d0 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -210,7 +210,7 @@ public TemplateWithArgs withArgs(A a, B b) { * @param tokens A list of tokens, which can be {@link String}s,boxed primitive types * (e.g. {@link Integer}), any {@link Token}, or {@link List}s * of any of these. - * @return The {@link TemplateBody} which captures the list of validated tokens. + * @return The {@link TemplateBody} which captures the list of validated {@link Token}s. * @throws IllegalArgumentException if the list of tokens contains an unexpected object. */ static TemplateBody body(Object... tokens) { From d6f06578defc9487a335ab6e56f6ec1eb63d71fe Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 18 Feb 2025 12:06:56 +0100 Subject: [PATCH 090/212] better Template documentation --- .../lib/template_framework/Template.java | 50 +++++++++++++++---- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 166964ecf73d0..de8142dbd23f6 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -29,8 +29,40 @@ import java.util.List; /** - * TODO - * TODO talk about hashtag replacement and arguments. + * {@link Template}s are used to generate code, based on {@link Token} which are rendered to {@link String}. + * + *

+ * A {@link Template} can have zero or more arguments, and for each number of arguments there is an implementation + * (e.g. {@link ZeroArgs} for zero arguments and {@link TwoArgs} for two arguments). This allows the use of Generics + * for the template argument types, i.e. the template arguments can be type checked. Ideally, we would have used + * String Templates to inject these arguments into the strings. But since String Templates are not (yet) available, + * the {@link Template}s provide hashtag replacements in the Strings: the {@link Template} argument + * names are captured, and the argument values automatically replace any {@code "#name"} in the Strings. See the + * different overloads of {@link make} for examples. Additional hashtag replacements can be defined with {@link let}. + * + *

+ * When using nested {@link Template}s, there can be collisions with identifiers (e.g. variable names and method names). + * For this, {@link Template}s provide dollar replacements, which automaticall rename any + * {@code "$name"} in the String with a {@code "name_ID"}, where the {@code "ID"} is unique for every use of + * a {@link Template}. The dollar replacement can also be captured with {@link $}, and passed to nested + * {@link Template}s, which allows sharing of these identifier names between {@link Template}s. + * + *

+ * To render a {@link Template} to a {@link String}, one first has to apply the arguments (e.g. with + * {@link TwoArgs#withArgs}) and then the resulting {@link TemplateWithArgs} can either be used as a + * {@link Token} inside another {@link Template}, or rendered to a {@link String} with {@link TemplateWithArgs#render}. + * + *

+ * A {@link TemplateBinding} allows the recurisve use of {@link Template}s. With the indirection of such a binding, + * a {@link Template} can reference itself. To ensure the termination of recursion, the templates are rendered + * with a certain amount of {@link fuel}, which is decreased at each {@link Template} nesting by a certain amount + * (can be changed with {@link setFuelCost}). Recursive templates are supposed to terminate once the {@link fuel} + * is depleated (i.e. reaches zero). + * + *

+ * Code generation often involves defining fields and variables, which are then available inside a defined + * scope, and can be sampled in any nested scope. To allow the use of names for multiple applications (e.g. + * fields, variables, methods, etc) ... TODO */ public interface Template { @@ -43,7 +75,7 @@ public interface Template { * {@snippet lang=java : * var template = Template.make(() -> body( * """ - * Multi-liine string or other tokens. + * Multi-line string or other tokens. * """ * )); * } @@ -66,7 +98,7 @@ static ZeroArgs make(Supplier body) { * {@snippet lang=java : * var template = Template.make("a", (Integer a) -> body( * """ - * Multi-liine string or other tokens. + * Multi-line string or other tokens. * We can use the hashtag replacement #a to directly insert the String value of a. * """, * "We can also use the captured parameter of a: " + a @@ -93,7 +125,7 @@ static OneArgs make(String arg0Name, Function body) { * {@snippet lang=java : * var template = Template.make("a", "b", (Integer a, String b) -> body( * """ - * Multi-liine string or other tokens. + * Multi-line string or other tokens. * We can use the hashtag replacement #a and #b to directly insert the String value of a and b. * """, * "We can also use the captured parameter of a and b: " + a + " and " + b @@ -218,12 +250,12 @@ static TemplateBody body(Object... tokens) { } /** - * Retrieves the renaming (dollar-replacement) of the {@code 'name'} for the + * Retrieves the dollar replacement of the {@code 'name'} for the * current {@link Template} that is being instanciated. It returns the same - * dollar-replacement as the string use {@code "$name"}. + * dollar replacement as the string use {@code "$name"}. * * Here an example where a {@link Template} creates a local variable {@code 'var'}, - * with an implicit dollar-replacement, and then captures that dollar-replacement + * with an implicit dollar replacement, and then captures that dollar replacement * using {@link $} for the use inside a nested template. * {@snippet lang=java : * var template = Template.make(() -> body( @@ -235,7 +267,7 @@ static TemplateBody body(Object... tokens) { * } * * @param name The {@link String} name of the name. - * @return The dollar-replacement for the {@code 'name'}. + * @return The dollar replacement for the {@code 'name'}. */ static String $(String name) { return Renderer.getCurrent().$(name); From df48a9214cbe1a3c59a47a8e5c34a0e2a5b62e90 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 18 Feb 2025 14:07:47 +0100 Subject: [PATCH 091/212] template library refactor --- .../lib/template_library/IRTestClass.java | 77 +++++++++++++++++++ .../lib/template_library/Library.java | 39 ++++++++++ .../Types.java} | 70 ++++------------- .../examples/TestTutorial.java | 33 ++++---- .../examples/TestFuzzExpression.java | 39 +++++----- .../examples/TestIRTestClass.java | 26 +++---- 6 files changed, 178 insertions(+), 106 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/IRTestClass.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/Library.java rename test/hotspot/jtreg/compiler/lib/{template_framework/Library.java => template_library/Types.java} (85%) rename test/hotspot/jtreg/testlibrary_tests/{template_framework => template_library}/examples/TestFuzzExpression.java (76%) rename test/hotspot/jtreg/testlibrary_tests/{template_framework => template_library}/examples/TestIRTestClass.java (87%) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/IRTestClass.java b/test/hotspot/jtreg/compiler/lib/template_library/IRTestClass.java new file mode 100644 index 0000000000000..ea7d451477962 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/IRTestClass.java @@ -0,0 +1,77 @@ +/* + * 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_library; + +import java.util.List; + +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateWithArgs; +import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.let; + +/** + * + */ +public abstract class IRTestClass { + public record Info(String classpath, String packageName, String className, List imports) {}; + + public static final Template.TwoArgs> TEMPLATE = + Template.make("info", "templates", (Info info, List templates) -> body( + let("classpath", info.classpath), + let("packageName", info.packageName), + let("className", info.className), + """ + package #packageName; + + // --- IMPORTS start --- + import compiler.lib.ir_framework.*; + """, + info.imports.stream().map(i -> "import " + i + ";\n").toList(), + """ + // --- IMPORTS end --- + + public class #className { + + // --- CLASS_HOOK insertions start --- + """, + Library.CLASS_HOOK.set( + """ + // --- CLASS_HOOK insertions end --- + + public static void main() { + TestFramework framework = new TestFramework(#className.class); + framework.addFlags("-classpath", "#classpath"); + framework.start(); + } + + // --- LIST OF TEMPLATES start --- + """, + templates + ), + """ + // --- LIST OF TEMPLATES end --- + } + """ + )); +} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Library.java b/test/hotspot/jtreg/compiler/lib/template_library/Library.java new file mode 100644 index 0000000000000..293408731aae2 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/Library.java @@ -0,0 +1,39 @@ +/* + * 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_library; + +import compiler.lib.template_framework.Hook; + +/** + * TODO + * The Library provides a collection of helpful Templates and Hooks. + * + * TODO more operators + * TODO more templates + * TODO configure choice and other randomization? + */ +public abstract class Library { + public static final Hook CLASS_HOOK = new Hook("Class"); + public static final Hook METHOD_HOOK = new Hook("Method"); +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java b/test/hotspot/jtreg/compiler/lib/template_library/Types.java similarity index 85% rename from test/hotspot/jtreg/compiler/lib/template_framework/Library.java rename to test/hotspot/jtreg/compiler/lib/template_library/Types.java index 8e2cd3202675a..f13d55491a0a9 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Types.java @@ -21,31 +21,32 @@ * questions. */ -package compiler.lib.template_framework; +package compiler.lib.template_library; import java.util.Arrays; import java.util.List; import java.util.Random; import jdk.test.lib.Utils; -import compiler.lib.generators.*; +import compiler.lib.generators.Generators; +import compiler.lib.generators.Generator; +import compiler.lib.generators.RestrictableGenerator; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateBinding; +import compiler.lib.template_framework.TemplateBody; import static compiler.lib.template_framework.Template.body; -import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; import static compiler.lib.template_framework.Template.fuel; import static compiler.lib.template_framework.Template.setFuelCost; +import static compiler.lib.template_framework.Template.defineName; import static compiler.lib.template_framework.Template.countNames; import static compiler.lib.template_framework.Template.sampleName; /** - * The Library provides a collection of helpful Templates and Hooks. * - * TODO more operators - * TODO more templates - * TODO configure choice and other randomization? */ -public abstract class Library { +public abstract class Types { private static final Random RANDOM = Utils.getRandomInstance(); private static final RestrictableGenerator GEN_INT = Generators.G.ints(); private static final RestrictableGenerator GEN_LONG = Generators.G.longs(); @@ -58,50 +59,6 @@ private static T choice(List list) { return list.get(i); } - public static final Hook CLASS_HOOK = new Hook("Class"); - public static final Hook METHOD_HOOK = new Hook("Method"); - - public record IRTestClassInfo(String classpath, String packageName, String className, List imports) {}; - - public static final Template.TwoArgs> IR_TEST_CLASS = - Template.make("info", "templates", (IRTestClassInfo info, List templates) -> body( - let("classpath", info.classpath), - let("packageName", info.packageName), - let("className", info.className), - """ - package #packageName; - - // --- IMPORTS start --- - import compiler.lib.ir_framework.*; - """, - info.imports.stream().map(i -> "import " + i + ";\n").toList(), - """ - // --- IMPORTS end --- - - public class #className { - - // --- CLASS_HOOK insertions start --- - """, - CLASS_HOOK.set( - """ - // --- CLASS_HOOK insertions end --- - - public static void main() { - TestFramework framework = new TestFramework(#className.class); - framework.addFlags("-classpath", "#classpath"); - framework.start(); - } - - // --- LIST OF TEMPLATES start --- - """, - templates - ), - """ - // --- LIST OF TEMPLATES end --- - } - """ - )); - public enum ExpressionType { INT("int"), LONG("long"), @@ -137,7 +94,7 @@ public enum ExpressionType { public static final Template.OneArgs GENERATE_BOOLEAN_USING_BOXING_INLINE = Template.make("name", (String name) -> body( - CLASS_HOOK.insert(DEFINE_STATIC_FIELD.withArgs(ExpressionType.BOOLEAN, $("flag"))), + Library.CLASS_HOOK.insert(DEFINE_STATIC_FIELD.withArgs(ExpressionType.BOOLEAN, $("flag"))), """ // #name is constant, but only known after Incremental Boxing Inline (after parsing). Integer $box; @@ -167,7 +124,7 @@ public enum ExpressionType { GENERATE_BOOLEAN_USING_BOXING_INLINE.withArgs(name), GENERATE_BOOLEAN_USING_EMPTY_LOOP.withArgs(name), // This will never constant fold, as it just loads a boolean: - CLASS_HOOK.insert(DEFINE_STATIC_FIELD.withArgs(ExpressionType.BOOLEAN, name)) + Library.CLASS_HOOK.insert(DEFINE_STATIC_FIELD.withArgs(ExpressionType.BOOLEAN, name)) )) )); @@ -184,8 +141,8 @@ public enum ExpressionType { public static final Template.TwoArgs GENERATE_EARLIER_VALUE = Template.make("type", "name", (ExpressionType type, String name) -> body( choice(List.of( - CLASS_HOOK.insert(DEFINE_STATIC_FIELD.withArgs(type, name)), - METHOD_HOOK.insert(GENERATE_EARLILER_VALUE_FROM_BOOLEAN.withArgs(type, name)) + Library.CLASS_HOOK.insert(DEFINE_STATIC_FIELD.withArgs(type, name)), + Library.METHOD_HOOK.insert(GENERATE_EARLILER_VALUE_FROM_BOOLEAN.withArgs(type, name)) )) )); @@ -351,4 +308,5 @@ private static List operators(ExpressionType type) { Template.make("type", (ExpressionType type) -> choice(operators(type)).instanciate()); static { OPERATOR_EXPRESSION_BINDING.bind(OPERATOR_EXPRESSION); } + } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java index 8bd4583b97c08..87c2bb79073b1 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java @@ -36,6 +36,7 @@ import java.util.List; import compiler.lib.compile_framework.*; + import compiler.lib.template_framework.Template; import compiler.lib.template_framework.Hook; import compiler.lib.template_framework.TemplateBinding; @@ -46,8 +47,8 @@ import static compiler.lib.template_framework.Template.defineName; import static compiler.lib.template_framework.Template.sampleName; import static compiler.lib.template_framework.Template.countNames; -import static compiler.lib.template_framework.Library.CLASS_HOOK; -import static compiler.lib.template_framework.Library.METHOD_HOOK; + +import compiler.lib.template_library.Library; public class TestTutorial { @@ -226,7 +227,7 @@ public static void main() { // In this example, we look at the use of Hooks. public static String generateWithCustomHooks() { // We can define a custom hook. - // Note: generally we prefer using the pre-defined CLASS_HOOK and METHOD_HOOK from the Library, + // Note: generally we prefer using the pre-defined Library.CLASS_HOOK and Library.METHOD_HOOK from the Library, // when ever possible. See also the example after this one. var myHook = new Hook("MyHook"); @@ -276,7 +277,7 @@ public static void main() { return templateClass.withArgs().render(); } - // We saw the use of custom hooks above, but now we look at the use of CLASS_HOOK and METHOD_HOOK + // We saw the use of custom hooks above, but now we look at the use of Library.CLASS_HOOK and Library.METHOD_HOOK // from the Temlate Library. public static String generateWithLibraryHooks() { var templateStaticField = Template.make("name", "value", (String name, Integer value) -> body( @@ -297,8 +298,8 @@ public static String generateWithLibraryHooks() { """ // Let's define a local variable $var and a static field $field. """, - CLASS_HOOK.insert(templateStaticField.withArgs($("field"), 5)), - METHOD_HOOK.insert(templateLocalVariable.withArgs($("var"), 11)), + Library.CLASS_HOOK.insert(templateStaticField.withArgs($("field"), 5)), + Library.METHOD_HOOK.insert(templateLocalVariable.withArgs($("var"), 11)), """ System.out.println("$field: " + $field); System.out.println("$var: " + $var); @@ -313,12 +314,12 @@ public static String generateWithLibraryHooks() { public class InnerTest5 { """, // Class Hook for fields. - CLASS_HOOK.set( + Library.CLASS_HOOK.set( """ public static void main() { """, // Method Hook for local variables, and earlier computations. - METHOD_HOOK.set( + Library.METHOD_HOOK.set( """ // This is the beginning of the "main" method body. System.out.println("Welcome to main!"); @@ -336,7 +337,7 @@ private static void other() { """, // Have a separate method hook for other, so that it can insert // its own local variables. - METHOD_HOOK.set( + Library.METHOD_HOOK.set( """ System.out.println("Welcome to other!"); """, @@ -438,10 +439,10 @@ public static String generateWithNames() { System.out.println("Starting inside main..."); """, templateStatus.withArgs(), - METHOD_HOOK.insert(templateLocalVariable.withArgs("int")), - METHOD_HOOK.insert(templateLocalVariable.withArgs("long")), - CLASS_HOOK.insert(templateStaticField.withArgs("int")), - CLASS_HOOK.insert(templateStaticField.withArgs("long")), + Library.METHOD_HOOK.insert(templateLocalVariable.withArgs("int")), + Library.METHOD_HOOK.insert(templateLocalVariable.withArgs("long")), + Library.CLASS_HOOK.insert(templateStaticField.withArgs("int")), + Library.CLASS_HOOK.insert(templateStaticField.withArgs("long")), templateStatus.withArgs(), // We should see a mix if fields and variables sampled. Collections.nCopies(5, templateSample.withArgs("int")), @@ -473,12 +474,12 @@ public static String generateWithNames() { public class InnerTest7 { """, // Class Hook for fields. - CLASS_HOOK.set( + Library.CLASS_HOOK.set( """ public static void main() { """, // Method Hook for local variables, and earlier computations. - METHOD_HOOK.set( + Library.METHOD_HOOK.set( """ // This is the beginning of the "main" method body. System.out.println("Welcome to main!"); @@ -496,7 +497,7 @@ private static void other() { """, // Have a separate method hook for other, so that it can insert // its own local variables. - METHOD_HOOK.set( + Library.METHOD_HOOK.set( """ System.out.println("Welcome to other!"); """, diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java similarity index 76% rename from test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java rename to test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index d2cb20fafe695..c9624fa998b54 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -30,27 +30,24 @@ * @compile ../../../compiler/lib/ir_framework/TestFramework.java * @compile ../../../compiler/lib/generators/Generators.java * @compile ../../../compiler/lib/verify/Verify.java - * @run driver template_framework.examples.TestFuzzExpression + * @run driver template_library.examples.TestFuzzExpression */ -package template_framework.examples; +package template_library.examples; import java.util.List; import java.util.ArrayList; -import compiler.lib.compile_framework.*; -import compiler.lib.generators.*; +import compiler.lib.compile_framework.CompileFramework; + import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateWithArgs; import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.let; -import static compiler.lib.template_framework.Library.CLASS_HOOK; -import static compiler.lib.template_framework.Library.METHOD_HOOK; -import compiler.lib.template_framework.Library.IRTestClassInfo; -import static compiler.lib.template_framework.Library.IR_TEST_CLASS; -import compiler.lib.template_framework.Library.ExpressionType; -import static compiler.lib.template_framework.Library.ALL_EXPRESSION_TYPES; -import static compiler.lib.template_framework.Library.EXPRESSION; + +import compiler.lib.template_library.Library; +import compiler.lib.template_library.IRTestClass; +import compiler.lib.template_library.Types; /** * This is a basic expression fuzzer: it generates random expressions using {@link Library.Expression}, @@ -83,19 +80,19 @@ public static String generate(CompileFramework comp) { // Create the info required for the test class. // It is imporant that we pass the classpath to the Test-VM, so that it has access // to all compiled classes. - IRTestClassInfo info = new IRTestClassInfo(comp.getEscapedClassPathOfCompiledClasses(), - "p.xyz", "InnerTest", - List.of("compiler.lib.generators.*", - "compiler.lib.verify.*")); + IRTestClass.Info info = new IRTestClass.Info(comp.getEscapedClassPathOfCompiledClasses(), + "p.xyz", "InnerTest", + List.of("compiler.lib.generators.*", + "compiler.lib.verify.*")); - var template1 = Template.make("type", (ExpressionType type)-> body( + var template1 = Template.make("type", (Types.ExpressionType type)-> body( """ // --- $test start --- // type: #type """, // We set a dedicated class hook here, so that fields are // NOT available across the tests. - CLASS_HOOK.set( + Library.CLASS_HOOK.set( """ private static Object $GOLD = $test(); @@ -103,12 +100,12 @@ public static String generate(CompileFramework comp) { @Test public static Object $test() { """, - METHOD_HOOK.set( + Library.METHOD_HOOK.set( // We need to catch Exceptions like ArithmeticException, so that we do // not get ExceptionInInitializerError when loading the class and running // the static code blocks. "try {\n", - " return ", EXPRESSION.withArgs(type), ";\n", + " return ", Types.EXPRESSION.withArgs(type), ";\n", """ } catch (Exception e) { return e; @@ -133,10 +130,10 @@ public static String generate(CompileFramework comp) { // Use template1 100 times with every type. List templates = new ArrayList<>(); for (int i = 0; i < 100; i++) { - for (ExpressionType type : ALL_EXPRESSION_TYPES) { + for (Types.ExpressionType type : Types.ALL_EXPRESSION_TYPES) { templates.add(template1.withArgs(type)); } } - return IR_TEST_CLASS.withArgs(info, templates).render(); + return IRTestClass.TEMPLATE.withArgs(info, templates).render(); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestIRTestClass.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestIRTestClass.java similarity index 87% rename from test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestIRTestClass.java rename to test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestIRTestClass.java index eceda50051b41..c9298324040c5 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestIRTestClass.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestIRTestClass.java @@ -29,23 +29,23 @@ * @compile ../../../compiler/lib/ir_framework/TestFramework.java * @compile ../../../compiler/lib/generators/Generators.java * @compile ../../../compiler/lib/verify/Verify.java - * @run driver template_framework.examples.TestIRTestClass + * @run driver template_library.examples.TestIRTestClass */ -package template_framework.examples; +package template_library.examples; import java.util.List; -import compiler.lib.compile_framework.*; -import compiler.lib.generators.*; +import compiler.lib.compile_framework.CompileFramework; + +import compiler.lib.generators.Generators; + import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateWithArgs; import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.let; -import static compiler.lib.template_framework.Library.CLASS_HOOK; -import static compiler.lib.template_framework.Library.METHOD_HOOK; -import static compiler.lib.template_framework.Library.IR_TEST_CLASS; -import compiler.lib.template_framework.Library.IRTestClassInfo; + +import compiler.lib.template_library.IRTestClass; /** * This is a basic IR verification test, in combination with Generators for random input generation @@ -78,10 +78,10 @@ public static String generate(CompileFramework comp) { // Create the info required for the test class. // It is imporant that we pass the classpath to the Test-VM, so that it has access // to all compiled classes. - IRTestClassInfo info = new IRTestClassInfo(comp.getEscapedClassPathOfCompiledClasses(), - "p.xyz", "InnerTest", - List.of("compiler.lib.generators.*", - "compiler.lib.verify.*")); + IRTestClass.Info info = new IRTestClass.Info(comp.getEscapedClassPathOfCompiledClasses(), + "p.xyz", "InnerTest", + List.of("compiler.lib.generators.*", + "compiler.lib.verify.*")); // We define a Test-Template: // - static fields for inputs: INPUT_A and INPUT_B @@ -144,6 +144,6 @@ public static String generate(CompileFramework comp) { List templates = ops.stream().map(op -> (TemplateWithArgs)template1.withArgs(op)).toList(); // Create the test class, which runs all templates. - return IR_TEST_CLASS.withArgs(info, templates).render(); + return IRTestClass.TEMPLATE.withArgs(info, templates).render(); } } From 3052633ea87351353797915110fc8186b6306ebe Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 18 Feb 2025 14:17:24 +0100 Subject: [PATCH 092/212] improve comments --- .../template_library/examples/TestFuzzExpression.java | 5 +++-- .../template_library/examples/TestIRTestClass.java | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index c9624fa998b54..7a352a74eac69 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -23,8 +23,9 @@ /* * @test - * @summary Test with example expressions that are fuzzed. The example expressions - * have inputs, which we can fill with random expressions. + * @summary Test the Template Library's expression generation. This test generates random + * expressions, and runs them with random inputs, comparing the results generated + * in the interpreter with the results generated by the compiled code. * @modules java.base/jdk.internal.misc * @library /test/lib / * @compile ../../../compiler/lib/ir_framework/TestFramework.java diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestIRTestClass.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestIRTestClass.java index c9298324040c5..95edd145198d0 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestIRTestClass.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestIRTestClass.java @@ -23,7 +23,7 @@ /* * @test - * @summary Test templates which creates many tests and runs them with the IR TestFramework. + * @summary Test IRTestClass.TEMPLATE which allows generating many tests and running them with the IR TestFramework. * @modules java.base/jdk.internal.misc * @library /test/lib / * @compile ../../../compiler/lib/ir_framework/TestFramework.java From a8129cb669c96610385cda8a6433046c844d1c49 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 18 Feb 2025 15:41:30 +0100 Subject: [PATCH 093/212] more documentation --- .../compiler/lib/template_framework/Code.java | 6 +++ .../lib/template_framework/CodeFrame.java | 40 +++++++++++++++++++ .../lib/template_framework/NameSet.java | 13 +++++- .../lib/template_framework/Renderer.java | 17 ++++---- .../template_framework/RendererException.java | 1 + .../lib/template_framework/TemplateFrame.java | 3 ++ .../lib/template_framework/Token.java | 6 +++ 7 files changed, 77 insertions(+), 9 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Code.java b/test/hotspot/jtreg/compiler/lib/template_framework/Code.java index 8b3b8387e680b..8d215265528bf 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Code.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Code.java @@ -26,6 +26,12 @@ import java.util.ArrayList; import java.util.List; +/** + * This class collects code, i.e. {@link String}s or {@link List}s of {@link String}s. + * All the Strings are later collected in a {@link StringBuilder}. If we used a {@link StringBuilder} + * directly to collect the Strings, we could not as easily insert code at an "earlier" position, i.e. + * reaching out to a {@link Hook#set}. + */ sealed interface Code permits Code.Token, Code.CodeList { record Token(String s) implements Code { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java index a3e382d5d050d..ce7979e4e1440 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java @@ -28,11 +28,35 @@ import java.util.ArrayList; import java.util.List; +/** + * The {@link CodeFrame} represents a frame (i.e. scope) of code, appending {@link Code} to the {@code 'codeList'} + * as {@link Token}s are rendered, and adding names to the {@link NameSet}s as they get defined by {@link Template#define}. + * {@link Hook}s can be added to a frame, which allows code to be inserted at that location later. + * When a {@link Hook} is {@link Hook#set}, this separates the {@link Template} into an outer and inner + * {@link CodeFrame}, ensuring that names that are {@link Template#defineName}'d inside the inner frame + * are only available inside that frame. + * + *

+ * On the other hand, each {@link TemplateFrame} represents the frame (or scope) of exactly one use of a + * {@link Template}. + * + *

+ * For simple {@link Template} nesting, the {@link CodeFrame}s and {@link TemplateFrame}s overlap exactly. + * However, when using {@link Hook#insert}, we simply nest {@link TemplateFrame}s, going further "in", + * but we jump to an outer {@link CodeFrame}, ensuring that we insert {@link Code} at the outer frame, + * and operating on the names of the outer frame. Once the {@link Hook#insert}ion is complete, we jump + * back to the caller {@link TemplateFrame} and {@link CodeFrame}. + */ class CodeFrame { public final CodeFrame parent; private final List codeList = new ArrayList(); private final Map hookCodeLists = new HashMap<>(); + /** + * The {@link NameSet}s are used for variable and fields etc. There is one set that + * contains only the mutable names, and another that contains all. This sampling only + * mutable names, or sampling from all names including immutable names. + */ final NameSet mutableNames; final NameSet allNames; @@ -53,14 +77,30 @@ private CodeFrame(CodeFrame parent, boolean isTransparentForNames) { } } + /** + * Creates a base frame, which has no {@link parent}. + */ public static CodeFrame makeBase() { return new CodeFrame(null, false); } + /** + * Creates a normal frame, which has a {@link parent} and which defines an inner + * {@link NameSet}, for the names that are generated inside this frame. Once this + * frame is exited, the name from inside this frame are not available any more. + */ public static CodeFrame make(CodeFrame parent) { return new CodeFrame(parent, false); } + /** + * Creates a special frame, which has a {@link parent} and but uses the {@link NameSet} + * from the parent frame, allowing {@link Template#defineName} to persist in the outer + * frame when the current frame is exited. This is necessary for {@link Hook#insert}, + * where we would possibly want to make field or variable definitions during the insertion + * that are not just local to the insertion but affect the {@link CodeFrame} that we + * {@link Hook#set} earlier and are now {@link Hook#insert}ing into. + */ public static CodeFrame makeTransparentForNames(CodeFrame parent) { return new CodeFrame(parent, true); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java b/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java index c7f8482fe5758..77cca99d938de 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java @@ -27,8 +27,17 @@ import java.util.Map; import java.util.ArrayList; import java.util.List; +import java.util.Random; +import jdk.test.lib.Utils; + +/** + * The {@link NameSet} defines a set of names (e.g. fields or variable names). They extend the + * set of the {@code 'parent'} set. + */ class NameSet { + static final Random RANDOM = Utils.getRandomInstance(); + private final NameSet parent; private final Map> names = new HashMap<>(); @@ -59,14 +68,14 @@ public String sample(Object type) { // Maybe sample from parent. if (parent != null) { int pc = parent.count(type); - int r = Renderer.RANDOM.nextInt(c); + int r = RANDOM.nextInt(c); if (r < pc) { return parent.sample(type); } } List locals = names.get(type); - int r = Renderer.RANDOM.nextInt(locals.size()); + int r = RANDOM.nextInt(locals.size()); return locals.get(r); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 5bb2232692b03..423b9d4afe4eb 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -29,9 +29,6 @@ import java.util.regex.MatchResult; import java.util.regex.Pattern; import java.util.stream.Stream; -import java.util.Random; - -import jdk.test.lib.Utils; /** * The {@link Renderer} class is used to keep track of the states during a nested @@ -45,8 +42,6 @@ class Renderer { private static final Pattern DOLLAR_NAME_PATTERN = Pattern.compile("\\$([a-zA-Z_][a-zA-Z0-9_]*)"); private static final Pattern HASHTAG_REPLACEMENT_PATTERN = Pattern.compile("#([a-zA-Z_][a-zA-Z0-9_]*)"); - static final Random RANDOM = Utils.getRandomInstance(); - /** * There can be at most one Renderer instance at any time. This is to avoid that users accidentally * render templates to strings, rather than letting them all render together. @@ -140,11 +135,19 @@ String sampleName(Object type, boolean onlyMutable) { return currentCodeFrame.sampleName(type, onlyMutable); } + /** + * Formats values to {@link String} with the goal of using them in Java code. + * By default we use the overrides of {@link Object#toString}. + * But for some boxed primitives we need to create a special formatting. + */ static String format(Object value) { return switch (value) { case String s -> s; case Integer i -> i.toString(); + // We need to append the "L" so that the values are not interpreted as ints, + // and then javac might complain that the values are too large for an int. case Long l -> l.toString() + "L"; + // Some Float and Double values like Infinity and NaN need a special representation. case Float f -> formatFloat(f); case Double d -> formatDouble(d); default -> value.toString(); @@ -188,8 +191,8 @@ private void renderTemplateWithArgs(TemplateWithArgs templateWithArgs) { currentTemplateFrame = templateFrame; templateWithArgs.visitArguments((name, value) -> addHashtagReplacement(name, format(value))); - TemplateBody it = templateWithArgs.instantiate(); - renderTokenList(it.tokens()); + TemplateBody body = templateWithArgs.instantiate(); + renderTokenList(body.tokens()); if (currentTemplateFrame != templateFrame) { throw new RuntimeException("Internal error: TemplateFrame mismatch!"); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/RendererException.java b/test/hotspot/jtreg/compiler/lib/template_framework/RendererException.java index 2d6d9f40165b8..04c36119869a1 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/RendererException.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/RendererException.java @@ -26,6 +26,7 @@ /** * This exception is thrown when something goes wrong during {@link Template} * rendering, or in the use of any of its static methods. + * It most likely indicates a wrong use of the {@link Template}s. */ public class RendererException extends RuntimeException { RendererException(String message) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java index fee81ea39fb1f..443b3df60d4c6 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java @@ -35,6 +35,9 @@ * The {@link fuel} is reduced over this chain, to give a heuristic on how much time * is spend on the code from the template corrsponding to the frame, and to give a * termination criterion to avoid nesting templates too deeply. + * + *

+ * See also {@link CodeFrame} for more explanations about the frames. */ class TemplateFrame { final TemplateFrame parent; diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java index 13bfded18b29f..cc35c6d6a3a3b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java @@ -27,6 +27,12 @@ import java.util.ArrayList; import java.util.List; +/** + * The {@link Template#body} and {@link Hook#set} are given a list of tokens, which are either + * {@link Token}s or {@link String}s or some permitted boxed primitives. These are then parsed + * and all non {@link Token}s are converted to {@link StringToken}s. The parsing also flattens + * {@link List}s. + */ sealed interface Token permits StringToken, TemplateWithArgs, TemplateWithArgs.ZeroArgsUse, From 4dcbbb74e4208833000574889ab7249c37db0055 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 19 Feb 2025 10:15:12 +0100 Subject: [PATCH 094/212] more README documentation --- .../hotspot/jtreg/compiler/lib/template_framework/README.md | 6 +++++- .../jtreg/compiler/lib/template_framework/Template.java | 6 ++++++ test/hotspot/jtreg/compiler/lib/template_library/README.md | 4 ++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/README.md diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/README.md b/test/hotspot/jtreg/compiler/lib/template_framework/README.md index 5ec4d4500f092..bf817fc71c029 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/README.md +++ b/test/hotspot/jtreg/compiler/lib/template_framework/README.md @@ -1,4 +1,8 @@ # Template Framework The Template Framework allows the generation of code with Templates. The goal is that these Templates are easy to write, and allow regression tests to cover a larger scope, and to make temlate based fuzzing easy to extend. -TODO +The Template Framework only generates code in the form of a String. This code can then be compiled and executed, for example with help of the [Compile Framework](../compile_framework/README.md). + +The basic functionalities of the Template Framework are described in the [Template Class](./Template.java), together with some examples. More examples can be found in [TestSimple.java](../../../testlibrary_tests/template_framework/examples/TestSimple.java) and [TestTutorial.java](../../../testlibrary_tests/template_framework/examples/TestTutorial.java). + +The [Template Library](../template_library/README.md) provides a large number of Templates which can be used to create anything from simple regression tests to complex fuzzers. diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index de8142dbd23f6..8f623c40b5b32 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -53,6 +53,12 @@ * {@link Token} inside another {@link Template}, or rendered to a {@link String} with {@link TemplateWithArgs#render}. * *

+ * A {@link TemplateWithArgs} can be used directly as a {@link Token} inside the {@link Template#body} to + * nest the {@link Template}s. Alternatively, code can be {@link Hook#insert}ed to where a {@link Hook} + * was {@link Hook#set} earlier (in some outer scope of the code). For example, while generating code in + * a method, one can reach out to the scope of the class, and insert a new field, or define a utility method. + * + *

* A {@link TemplateBinding} allows the recurisve use of {@link Template}s. With the indirection of such a binding, * a {@link Template} can reference itself. To ensure the termination of recursion, the templates are rendered * with a certain amount of {@link fuel}, which is decreased at each {@link Template} nesting by a certain amount diff --git a/test/hotspot/jtreg/compiler/lib/template_library/README.md b/test/hotspot/jtreg/compiler/lib/template_library/README.md new file mode 100644 index 0000000000000..edf3c123e0354 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/README.md @@ -0,0 +1,4 @@ +# Template Library +The Template Library is an extension to the [Template Framework](../template_framework/README.md), and provides a large number of Templates and Template generators. These can be used to create anything from simple regression tests to complex fuzzers. + +TODO From 3521418c5238385b6f2a9d831f2646fec4d58835 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 20 Feb 2025 05:47:07 +0100 Subject: [PATCH 095/212] wip types refactor --- .../lib/template_library/Expressions.java | 46 ++++++++++++++++++ .../compiler/lib/template_library/README.md | 4 ++ .../lib/template_library/types/Int.java | 40 ++++++++++++++++ .../lib/template_library/types/Operation.java | 33 +++++++++++++ .../lib/template_library/types/Type.java | 48 +++++++++++++++++++ .../examples/TestFuzzExpression.java | 9 ++-- 6 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/Expressions.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/Int.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/Type.java diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java new file mode 100644 index 0000000000000..73335b2529352 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.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_library; + +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateWithArgs; +import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.$; +import static compiler.lib.template_framework.Template.fuel; +import static compiler.lib.template_framework.Template.setFuelCost; +import static compiler.lib.template_framework.Template.defineName; +import static compiler.lib.template_framework.Template.countNames; +import static compiler.lib.template_framework.Template.sampleName; + +import compiler.lib.template_library.types.Type; + +public abstract class Expressions { + + public static final TemplateWithArgs expression(Type type) { + return Template.make(() -> body( + type.con() + )).withArgs(); + } + +} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/README.md b/test/hotspot/jtreg/compiler/lib/template_library/README.md index edf3c123e0354..92a43ebdc778b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/README.md +++ b/test/hotspot/jtreg/compiler/lib/template_library/README.md @@ -1,4 +1,8 @@ # Template Library The Template Library is an extension to the [Template Framework](../template_framework/README.md), and provides a large number of Templates and Template generators. These can be used to create anything from simple regression tests to complex fuzzers. +## Examples + +See [TestIRTestClass.java](../../../testlibrary_tests/template_library/examples/TestIRTestClass.java) to see a simple Templated regression test that uses the Library Template from [IRTestClass.java](./IRTestClass.java) and makes use of the [IR Framework](../ir_framework/README.md), [Generators](../generators/Generators.java), and [Verify](../verify/Verify.java). + TODO diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java b/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java new file mode 100644 index 0000000000000..77cf7c3072d9b --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java @@ -0,0 +1,40 @@ +/* + * 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_library.types; + +import compiler.lib.generators.Generators; +import compiler.lib.generators.RestrictableGenerator; + +public final class Int extends Type { + public static final Int INSTANCE = new Int(); + private static final RestrictableGenerator GEN_INT = Generators.G.ints(); + + @Override + public final String name() { return "int"; } + + @Override + public final Object con() { + return GEN_INT.next(); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java b/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java new file mode 100644 index 0000000000000..319776f4edafb --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java @@ -0,0 +1,33 @@ +/* + * 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_library.types; + +abstract class Operator { + public abstract TemplateWithArgs instanciate(); + + public static class Unary extends Operator { + @Override + + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/Type.java b/test/hotspot/jtreg/compiler/lib/template_library/types/Type.java new file mode 100644 index 0000000000000..eb68d72eb88ed --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/Type.java @@ -0,0 +1,48 @@ +/* + * 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_library.types; + +import java.util.List; + +/** + * The {@link Type} abstract class defines the basic functionalities that any {@link Type} + * must provide, such as generating random expressions. + * + * TODO + */ +public abstract class Type { + + public static final List primitives() { + return List.of(Int.INSTANCE); + } + + /** + * Returns name of the type that can be used in Java code. + * + * @return The name of the type that can be used in Java code. + */ + public abstract String name(); + + public abstract Object con(); +} diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index 7a352a74eac69..2ba93062d7a54 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -48,7 +48,8 @@ import compiler.lib.template_library.Library; import compiler.lib.template_library.IRTestClass; -import compiler.lib.template_library.Types; +import compiler.lib.template_library.types.Type; +import compiler.lib.template_library.Expressions; /** * This is a basic expression fuzzer: it generates random expressions using {@link Library.Expression}, @@ -86,7 +87,7 @@ public static String generate(CompileFramework comp) { List.of("compiler.lib.generators.*", "compiler.lib.verify.*")); - var template1 = Template.make("type", (Types.ExpressionType type)-> body( + var template1 = Template.make("type", (Type type)-> body( """ // --- $test start --- // type: #type @@ -106,7 +107,7 @@ public static String generate(CompileFramework comp) { // not get ExceptionInInitializerError when loading the class and running // the static code blocks. "try {\n", - " return ", Types.EXPRESSION.withArgs(type), ";\n", + " return ", Expressions.expression(type), ";\n", """ } catch (Exception e) { return e; @@ -131,7 +132,7 @@ public static String generate(CompileFramework comp) { // Use template1 100 times with every type. List templates = new ArrayList<>(); for (int i = 0; i < 100; i++) { - for (Types.ExpressionType type : Types.ALL_EXPRESSION_TYPES) { + for (Type type : Type.primitives()) { templates.add(template1.withArgs(type)); } } From 94df4226acc5ec4572699e8f33250e4b872f06b4 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 20 Feb 2025 08:15:58 +0100 Subject: [PATCH 096/212] small change --- .../jtreg/compiler/lib/template_library/types/Operation.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java b/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java index 319776f4edafb..089c119aeacae 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java @@ -28,6 +28,8 @@ abstract class Operator { public static class Unary extends Operator { @Override - + public final TemplateWithArgs instanciate() { + return null; + } } } From 9b4bed84cd3d5189e2b5ef2434804443809bb313 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 20 Feb 2025 16:25:55 +0100 Subject: [PATCH 097/212] more expression code wip --- .../lib/template_library/Expression.java | 59 ++++++++++++++ .../lib/template_library/Expressions.java | 46 ++++++++++- .../lib/template_library/types/Int.java | 12 +++ .../lib/template_library/types/Operation.java | 12 +-- .../lib/template_library/types/Type.java | 2 + .../examples/TestFuzzExpression.java | 79 ++++++++++--------- 6 files changed, 165 insertions(+), 45 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/Expression.java diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java new file mode 100644 index 0000000000000..b2644822febfb --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -0,0 +1,59 @@ +/* + * 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_library; + +import java.util.List; + +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateWithArgs; +import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.$; +import static compiler.lib.template_framework.Template.fuel; +import static compiler.lib.template_framework.Template.setFuelCost; +import static compiler.lib.template_framework.Template.defineName; +import static compiler.lib.template_framework.Template.countNames; +import static compiler.lib.template_framework.Template.sampleName; + +import compiler.lib.template_library.types.Type; + +/** + * TODO: description + * Idea: generates a template that has a list of {@link Type} holes. + */ +public final class Expression { + private final Template.OneArgs> template; + private final List types; + + Expression(final Template.OneArgs> template, final List types) { + this.template = template; + this.types = types; + } + + public final List types() { return this.types; } + + public final TemplateWithArgs withArgs(List args) { + // TODO: check length? + return template.withArgs(args); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java index 73335b2529352..814ab5e01b08c 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java @@ -23,6 +23,10 @@ package compiler.lib.template_library; +import java.util.List; +import java.util.ArrayList; +import java.util.HashSet; + import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateWithArgs; import static compiler.lib.template_framework.Template.body; @@ -34,13 +38,49 @@ import static compiler.lib.template_framework.Template.sampleName; import compiler.lib.template_library.types.Type; +import compiler.lib.template_library.types.Operation; public abstract class Expressions { - public static final TemplateWithArgs expression(Type type) { - return Template.make(() -> body( + public static final Expression constant(Type type) { + var template = Template.make("args", (List args) -> body( type.con() - )).withArgs(); + )); + return new Expression(template, List.of()); + } + + private interface ExpressionGenerator { + List tokens(List args); + } + + private interface ExpressionGeneratorStep { + void addTokens(List tokens, List args); + } + + public static final Expression expression(Type resultType, List allowedTypes, int maxDepth) { + HashSet allowedTypesSet = new HashSet(allowedTypes); + + List types = new ArrayList(); + ExpressionGenerator generator = expressionGenerator(resultType, allowedTypesSet, maxDepth, types); + // TODO: + // Output: lambda(args) -> list of tokens, using args + // Step: lambda(args, tokens) -> update tokens + + var template = Template.make("args", (List args) -> body( + generator.tokens(args) + )); + return new Expression(template, List.of()); + } + + private static final ExpressionGenerator expressionGenerator(Type resultType, HashSet allowedTypes, int maxDepth, List types) { + return (List args) -> { + List tokens = new ArrayList(); + tokens.add(resultType.con()); + return tokens; + }; } + // List ops = resultType.operations().stream().filter(o -> o.hasOnlyTypes(allowedTypesSet)).toList(); + // return null; + //} } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java b/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java index 77cf7c3072d9b..1546a4a9c1246 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java @@ -23,6 +23,8 @@ package compiler.lib.template_library.types; +import java.util.List; + import compiler.lib.generators.Generators; import compiler.lib.generators.RestrictableGenerator; @@ -30,6 +32,11 @@ public final class Int extends Type { public static final Int INSTANCE = new Int(); private static final RestrictableGenerator GEN_INT = Generators.G.ints(); + private static final List OPERATIONS = List.of( + new Operation.Unary("(-(", Int.INSTANCE, "))"), + new Operation.Unary("(~", Int.INSTANCE, ")") + ); + @Override public final String name() { return "int"; } @@ -37,4 +44,9 @@ public final class Int extends Type { public final Object con() { return GEN_INT.next(); } + + @Override + public final List operations() { + return OPERATIONS; + } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java b/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java index 089c119aeacae..c700a8de25233 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java @@ -23,13 +23,15 @@ package compiler.lib.template_library.types; -abstract class Operator { - public abstract TemplateWithArgs instanciate(); +import java.util.HashSet; - public static class Unary extends Operator { +public sealed interface Operation permits Operation.Unary { + boolean hasOnlyTypes(HashSet types); + + public static record Unary(String s0, Type t0, String s1) implements Operation { @Override - public final TemplateWithArgs instanciate() { - return null; + public boolean hasOnlyTypes(HashSet types) { + return types.contains(t0); } } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/Type.java b/test/hotspot/jtreg/compiler/lib/template_library/types/Type.java index eb68d72eb88ed..03e0b1c2fb481 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/Type.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/Type.java @@ -45,4 +45,6 @@ public static final List primitives() { public abstract String name(); public abstract Object con(); + + public abstract List operations(); } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index 2ba93062d7a54..341cc93f37d51 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -49,6 +49,7 @@ import compiler.lib.template_library.Library; import compiler.lib.template_library.IRTestClass; import compiler.lib.template_library.types.Type; +import compiler.lib.template_library.Expression; import compiler.lib.template_library.Expressions; /** @@ -87,47 +88,51 @@ public static String generate(CompileFramework comp) { List.of("compiler.lib.generators.*", "compiler.lib.verify.*")); - var template1 = Template.make("type", (Type type)-> body( - """ - // --- $test start --- - // type: #type - """, - // We set a dedicated class hook here, so that fields are - // NOT available across the tests. - Library.CLASS_HOOK.set( - """ - - private static Object $GOLD = $test(); - - @Test - public static Object $test() { - """, - Library.METHOD_HOOK.set( - // We need to catch Exceptions like ArithmeticException, so that we do - // not get ExceptionInInitializerError when loading the class and running - // the static code blocks. - "try {\n", - " return ", Expressions.expression(type), ";\n", + var template1 = Template.make("type", (Type type)-> { + Expression exp = Expressions.expression(type, Type.primitives(), 2); + return body( """ - } catch (Exception e) { - return e; - } + // --- $test start --- + // type: #type + """, + // We set a dedicated class hook here, so that fields are + // NOT available across the tests. + Library.CLASS_HOOK.set( """ - ), - """ - } - @Check(test = "$test") - public static void $check(Object result) { - Verify.checkEQ(result, $GOLD); - } + private static Object $GOLD = $test(); + + @Test + public static Object $test() { + """, + Library.METHOD_HOOK.set( + // We need to catch Exceptions like ArithmeticException, so that we do + // not get ExceptionInInitializerError when loading the class and running + // the static code blocks. + "try {\n", + //" return ", Expressions.constant(type).withArgs(List.of()), ";\n", + " return ", exp.withArgs(List.of()), ";\n", + """ + } catch (Exception e) { + return e; + } + """ + ), + """ + } - """ - ), - """ - // --- $test end --- - """ - )); + @Check(test = "$test") + public static void $check(Object result) { + Verify.checkEQ(result, $GOLD); + } + + """ + ), + """ + // --- $test end --- + """ + ); + }); // Use template1 100 times with every type. List templates = new ArrayList<>(); From fc4b3c918e8ae50b4ec0fa5ce96808174445b770 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 20 Feb 2025 16:36:52 +0100 Subject: [PATCH 098/212] use expression twice --- .../lib/template_library/Expressions.java | 3 +- .../examples/TestFuzzExpression.java | 34 ++++++++----------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java index 814ab5e01b08c..916a49d43894b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java @@ -73,9 +73,10 @@ public static final Expression expression(Type resultType, List allowedTyp } private static final ExpressionGenerator expressionGenerator(Type resultType, HashSet allowedTypes, int maxDepth, List types) { + Object c = resultType.con(); return (List args) -> { List tokens = new ArrayList(); - tokens.add(resultType.con()); + tokens.add(c); return tokens; }; } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index 341cc93f37d51..ffb180711da28 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -94,41 +94,35 @@ public static String generate(CompileFramework comp) { """ // --- $test start --- // type: #type + + @DontCompile + public static Object $reference() { + try { """, - // We set a dedicated class hook here, so that fields are - // NOT available across the tests. - Library.CLASS_HOOK.set( + " return ", exp.withArgs(List.of()), ";\n", """ - - private static Object $GOLD = $test(); + } catch (Exception e) { + return e; + } + } @Test public static Object $test() { + try { """, - Library.METHOD_HOOK.set( - // We need to catch Exceptions like ArithmeticException, so that we do - // not get ExceptionInInitializerError when loading the class and running - // the static code blocks. - "try {\n", - //" return ", Expressions.constant(type).withArgs(List.of()), ";\n", - " return ", exp.withArgs(List.of()), ";\n", - """ + " return ", exp.withArgs(List.of()), ";\n", + """ } catch (Exception e) { return e; } - """ - ), - """ } @Check(test = "$test") public static void $check(Object result) { - Verify.checkEQ(result, $GOLD); + Object gold = $reference(); + Verify.checkEQ(result, gold); } - """ - ), - """ // --- $test end --- """ ); From 0d19345981537b9a2a47e7b812387a810dbe4ffa Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 20 Feb 2025 17:02:04 +0100 Subject: [PATCH 099/212] random unary expressions --- .../lib/template_library/Expressions.java | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java index 916a49d43894b..1236b1190e489 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java @@ -26,6 +26,8 @@ import java.util.List; import java.util.ArrayList; import java.util.HashSet; +import java.util.Random; +import jdk.test.lib.Utils; import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateWithArgs; @@ -42,6 +44,14 @@ public abstract class Expressions { + private static final Random RANDOM = Utils.getRandomInstance(); + + private static T choice(List list) { + if (list.isEmpty()) { return null; } + int i = RANDOM.nextInt(list.size()); + return list.get(i); + } + public static final Expression constant(Type type) { var template = Template.make("args", (List args) -> body( type.con() @@ -73,15 +83,36 @@ public static final Expression expression(Type resultType, List allowedTyp } private static final ExpressionGenerator expressionGenerator(Type resultType, HashSet allowedTypes, int maxDepth, List types) { - Object c = resultType.con(); + ExpressionGeneratorStep step = expressionGeneratorStep(resultType, allowedTypes, maxDepth, types); return (List args) -> { List tokens = new ArrayList(); - tokens.add(c); + step.addTokens(tokens, args); return tokens; }; } - // List ops = resultType.operations().stream().filter(o -> o.hasOnlyTypes(allowedTypesSet)).toList(); - // return null; - //} + private static final ExpressionGeneratorStep expressionGeneratorStep(Type resultType, HashSet allowedTypes, int maxDepth, List types) { + List ops = resultType.operations().stream().filter(o -> o.hasOnlyTypes(allowedTypes)).toList(); + if (maxDepth <= 0 || ops.isEmpty()) { + // TODO: add to types in some cases! + return expressionGeneratorStepCon(resultType); + } + switch (choice(ops)) { + case Operation.Unary(String s0, Type t0, String s1) -> { + ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, allowedTypes, maxDepth-1, types); + return (List tokens, List args) -> { + tokens.add(s0); + step0.addTokens(tokens, args); + tokens.add(s1); + }; + } + } + } + + private static final ExpressionGeneratorStep expressionGeneratorStepCon(Type type) { + Object c = type.con(); + return (List tokens, List args) -> { + tokens.add(c); + }; + } } From c9fd16f17307734d5706793c591300300531ab0b Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 20 Feb 2025 17:09:54 +0100 Subject: [PATCH 100/212] binary and ternary operation --- .../lib/template_library/Expressions.java | 25 +++++++++++++++++++ .../lib/template_library/types/Int.java | 2 ++ .../lib/template_library/types/Operation.java | 21 +++++++++++++++- 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java index 1236b1190e489..c2ebcd16620fb 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java @@ -106,6 +106,31 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result tokens.add(s1); }; } + case Operation.Binary(String s0, Type t0, String s1, Type t1, String s2) -> { + ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, allowedTypes, maxDepth-1, types); + ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, allowedTypes, maxDepth-1, types); + return (List tokens, List args) -> { + tokens.add(s0); + step0.addTokens(tokens, args); + tokens.add(s1); + step1.addTokens(tokens, args); + tokens.add(s2); + }; + } + case Operation.Ternary(String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3) -> { + ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, allowedTypes, maxDepth-1, types); + ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, allowedTypes, maxDepth-1, types); + ExpressionGeneratorStep step2 = expressionGeneratorStep(t1, allowedTypes, maxDepth-1, types); + return (List tokens, List args) -> { + tokens.add(s0); + step0.addTokens(tokens, args); + tokens.add(s1); + step1.addTokens(tokens, args); + tokens.add(s2); + step2.addTokens(tokens, args); + tokens.add(s3); + }; + } } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java b/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java index 1546a4a9c1246..1c612dd602c7f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java @@ -35,6 +35,8 @@ public final class Int extends Type { private static final List OPERATIONS = List.of( new Operation.Unary("(-(", Int.INSTANCE, "))"), new Operation.Unary("(~", Int.INSTANCE, ")") + + ); @Override diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java b/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java index c700a8de25233..f739ac3a19249 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java @@ -25,7 +25,9 @@ import java.util.HashSet; -public sealed interface Operation permits Operation.Unary { +public sealed interface Operation permits Operation.Unary, + Operation.Binary, + Operation.Ternary { boolean hasOnlyTypes(HashSet types); public static record Unary(String s0, Type t0, String s1) implements Operation { @@ -34,4 +36,21 @@ public boolean hasOnlyTypes(HashSet types) { return types.contains(t0); } } + + public static record Binary(String s0, Type t0, String s1, Type t1, String s2) implements Operation { + @Override + public boolean hasOnlyTypes(HashSet types) { + return types.contains(t0) && + types.contains(t1); + } + } + + public static record Ternary(String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3) implements Operation { + @Override + public boolean hasOnlyTypes(HashSet types) { + return types.contains(t0) && + types.contains(t1) && + types.contains(t2); + } + } } From 6991e200a43770ca95e86f6eea8a997522e9b739 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 21 Feb 2025 08:12:10 +0100 Subject: [PATCH 101/212] some binary int ops --- .../compiler/lib/template_library/types/Int.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java b/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java index 1c612dd602c7f..39ea412d3af94 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java @@ -34,9 +34,22 @@ public final class Int extends Type { private static final List OPERATIONS = List.of( new Operation.Unary("(-(", Int.INSTANCE, "))"), - new Operation.Unary("(~", Int.INSTANCE, ")") + new Operation.Unary("(~", Int.INSTANCE, ")"), + new Operation.Binary("(", Int.INSTANCE, " + ", Int.INSTANCE, ")"), + new Operation.Binary("(", Int.INSTANCE, " - ", Int.INSTANCE, ")"), + new Operation.Binary("(", Int.INSTANCE, " * ", Int.INSTANCE, ")"), + new Operation.Binary("(", Int.INSTANCE, " / ", Int.INSTANCE, ")"), + new Operation.Binary("(", Int.INSTANCE, " % ", Int.INSTANCE, ")"), + new Operation.Binary("(", Int.INSTANCE, " & ", Int.INSTANCE, ")"), + new Operation.Binary("(", Int.INSTANCE, " | ", Int.INSTANCE, ")"), + new Operation.Binary("(", Int.INSTANCE, " ^ ", Int.INSTANCE, ")"), + new Operation.Binary("(", Int.INSTANCE, " << ", Int.INSTANCE, ")"), + new Operation.Binary("(", Int.INSTANCE, " >> ", Int.INSTANCE, ")"), + new Operation.Binary("(", Int.INSTANCE, " >>> ", Int.INSTANCE, ")") + // TODO: + // new TernaryOperator("(", ExpressionType.BOOLEAN, " ? ", ExpressionType.INT, " : ", ExpressionType.INT, ")") ); @Override From a2d2f26eb5e136cbfddf8b44862cadd889b5cff5 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 21 Feb 2025 08:25:36 +0100 Subject: [PATCH 102/212] move files around --- .../jtreg/compiler/lib/template_library/Expression.java | 2 -- .../jtreg/compiler/lib/template_library/Expressions.java | 3 --- .../compiler/lib/template_library/{types => }/Operation.java | 2 +- .../jtreg/compiler/lib/template_library/{types => }/Type.java | 4 +++- .../jtreg/compiler/lib/template_library/types/Int.java | 3 +++ .../template_library/examples/TestFuzzExpression.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) rename test/hotspot/jtreg/compiler/lib/template_library/{types => }/Operation.java (97%) rename test/hotspot/jtreg/compiler/lib/template_library/{types => }/Type.java (94%) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index b2644822febfb..d0646d745e291 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -35,8 +35,6 @@ import static compiler.lib.template_framework.Template.countNames; import static compiler.lib.template_framework.Template.sampleName; -import compiler.lib.template_library.types.Type; - /** * TODO: description * Idea: generates a template that has a list of {@link Type} holes. diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java index c2ebcd16620fb..9916f461805a1 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java @@ -39,9 +39,6 @@ import static compiler.lib.template_framework.Template.countNames; import static compiler.lib.template_framework.Template.sampleName; -import compiler.lib.template_library.types.Type; -import compiler.lib.template_library.types.Operation; - public abstract class Expressions { private static final Random RANDOM = Utils.getRandomInstance(); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java b/test/hotspot/jtreg/compiler/lib/template_library/Operation.java similarity index 97% rename from test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java rename to test/hotspot/jtreg/compiler/lib/template_library/Operation.java index f739ac3a19249..50e8476bc2229 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/Operation.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operation.java @@ -21,7 +21,7 @@ * questions. */ -package compiler.lib.template_library.types; +package compiler.lib.template_library; import java.util.HashSet; diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/Type.java b/test/hotspot/jtreg/compiler/lib/template_library/Type.java similarity index 94% rename from test/hotspot/jtreg/compiler/lib/template_library/types/Type.java rename to test/hotspot/jtreg/compiler/lib/template_library/Type.java index 03e0b1c2fb481..187c4c1e599e8 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/Type.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Type.java @@ -21,10 +21,12 @@ * questions. */ -package compiler.lib.template_library.types; +package compiler.lib.template_library; import java.util.List; +import compiler.lib.template_library.types.Int; + /** * The {@link Type} abstract class defines the basic functionalities that any {@link Type} * must provide, such as generating random expressions. diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java b/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java index 39ea412d3af94..45726ce75222d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java @@ -28,6 +28,9 @@ import compiler.lib.generators.Generators; import compiler.lib.generators.RestrictableGenerator; +import compiler.lib.template_library.Type; +import compiler.lib.template_library.Operation; + public final class Int extends Type { public static final Int INSTANCE = new Int(); private static final RestrictableGenerator GEN_INT = Generators.G.ints(); diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index ffb180711da28..b5481f784ea30 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -48,7 +48,7 @@ import compiler.lib.template_library.Library; import compiler.lib.template_library.IRTestClass; -import compiler.lib.template_library.types.Type; +import compiler.lib.template_library.Type; import compiler.lib.template_library.Expression; import compiler.lib.template_library.Expressions; From 84a4ec371bfc6820c96c34e1cef82aab0d728ab4 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 21 Feb 2025 09:12:23 +0100 Subject: [PATCH 103/212] more types --- .../compiler/lib/template_library/Type.java | 17 ++++- .../template_library/types/BooleanType.java | 61 ++++++++++++++++ .../template_library/types/DoubleType.java | 62 +++++++++++++++++ .../lib/template_library/types/FloatType.java | 62 +++++++++++++++++ .../types/{Int.java => IntType.java} | 33 +++++---- .../lib/template_library/types/LongType.java | 69 +++++++++++++++++++ 6 files changed, 285 insertions(+), 19 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java rename test/hotspot/jtreg/compiler/lib/template_library/types/{Int.java => IntType.java} (57%) create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Type.java b/test/hotspot/jtreg/compiler/lib/template_library/Type.java index 187c4c1e599e8..11dae41469322 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Type.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Type.java @@ -25,7 +25,11 @@ import java.util.List; -import compiler.lib.template_library.types.Int; +import compiler.lib.template_library.types.IntType; +import compiler.lib.template_library.types.LongType; +import compiler.lib.template_library.types.FloatType; +import compiler.lib.template_library.types.DoubleType; +import compiler.lib.template_library.types.BooleanType; /** * The {@link Type} abstract class defines the basic functionalities that any {@link Type} @@ -36,7 +40,13 @@ public abstract class Type { public static final List primitives() { - return List.of(Int.INSTANCE); + return List.of( + IntType.INSTANCE, + LongType.INSTANCE, + FloatType.INSTANCE, + DoubleType.INSTANCE, + BooleanType.INSTANCE + ); } /** @@ -46,6 +56,9 @@ public static final List primitives() { */ public abstract String name(); + @Override + public final String toString() { return name(); } + public abstract Object con(); public abstract List operations(); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java new file mode 100644 index 0000000000000..8093590c25213 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java @@ -0,0 +1,61 @@ +/* + * 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_library.types; + +import java.util.List; + +import java.util.Random; +import jdk.test.lib.Utils; + +import compiler.lib.template_library.Type; +import compiler.lib.template_library.Operation; + +public final class BooleanType extends Type { + public static final BooleanType INSTANCE = new BooleanType(); + private static final Random RANDOM = Utils.getRandomInstance(); + + private static final List OPERATIONS = List.of( + new Operation.Unary("(!(", BooleanType.INSTANCE, "))"), + + new Operation.Binary("(", BooleanType.INSTANCE, " || ", BooleanType.INSTANCE, ")"), + new Operation.Binary("(", BooleanType.INSTANCE, " && ", BooleanType.INSTANCE, ")"), + new Operation.Binary("(", BooleanType.INSTANCE, " ^ ", BooleanType.INSTANCE, ")"), + + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", BooleanType.INSTANCE, " : ", BooleanType.INSTANCE, ")") + ); + + @Override + public final String name() { return "boolean"; } + + @Override + public final Object con() { + // TODO: generator for boolean? Could have different probabilities! + return RANDOM.nextInt() % 2 == 0; + } + + @Override + public final List operations() { + return OPERATIONS; + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java new file mode 100644 index 0000000000000..fe37f4dc35d2f --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java @@ -0,0 +1,62 @@ +/* + * 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_library.types; + +import java.util.List; + +import compiler.lib.generators.Generators; +import compiler.lib.generators.Generator; + +import compiler.lib.template_library.Type; +import compiler.lib.template_library.Operation; + +public final class DoubleType extends Type { + public static final DoubleType INSTANCE = new DoubleType(); + private static final Generator GEN_DOUBLE = Generators.G.doubles(); + + private static final List OPERATIONS = List.of( + new Operation.Unary("(-(", DoubleType.INSTANCE, "))"), + + new Operation.Binary("(", DoubleType.INSTANCE, " + ", DoubleType.INSTANCE, ")"), + new Operation.Binary("(", DoubleType.INSTANCE, " - ", DoubleType.INSTANCE, ")"), + new Operation.Binary("(", DoubleType.INSTANCE, " * ", DoubleType.INSTANCE, ")"), + new Operation.Binary("(", DoubleType.INSTANCE, " / ", DoubleType.INSTANCE, ")"), + new Operation.Binary("(", DoubleType.INSTANCE, " % ", DoubleType.INSTANCE, ")"), + + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", DoubleType.INSTANCE, " : ", DoubleType.INSTANCE, ")") + ); + + @Override + public final String name() { return "double"; } + + @Override + public final Object con() { + return GEN_DOUBLE.next(); + } + + @Override + public final List operations() { + return OPERATIONS; + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java new file mode 100644 index 0000000000000..a4b94787ec775 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java @@ -0,0 +1,62 @@ +/* + * 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_library.types; + +import java.util.List; + +import compiler.lib.generators.Generators; +import compiler.lib.generators.Generator; + +import compiler.lib.template_library.Type; +import compiler.lib.template_library.Operation; + +public final class FloatType extends Type { + public static final FloatType INSTANCE = new FloatType(); + private static final Generator GEN_FLOAT = Generators.G.floats(); + + private static final List OPERATIONS = List.of( + new Operation.Unary("(-(", FloatType.INSTANCE, "))"), + + new Operation.Binary("(", FloatType.INSTANCE, " + ", FloatType.INSTANCE, ")"), + new Operation.Binary("(", FloatType.INSTANCE, " - ", FloatType.INSTANCE, ")"), + new Operation.Binary("(", FloatType.INSTANCE, " * ", FloatType.INSTANCE, ")"), + new Operation.Binary("(", FloatType.INSTANCE, " / ", FloatType.INSTANCE, ")"), + new Operation.Binary("(", FloatType.INSTANCE, " % ", FloatType.INSTANCE, ")"), + + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", FloatType.INSTANCE, " : ", FloatType.INSTANCE, ")") + ); + + @Override + public final String name() { return "float"; } + + @Override + public final Object con() { + return GEN_FLOAT.next(); + } + + @Override + public final List operations() { + return OPERATIONS; + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java b/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java similarity index 57% rename from test/hotspot/jtreg/compiler/lib/template_library/types/Int.java rename to test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java index 45726ce75222d..cbbe7d3e8896e 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/Int.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java @@ -31,28 +31,27 @@ import compiler.lib.template_library.Type; import compiler.lib.template_library.Operation; -public final class Int extends Type { - public static final Int INSTANCE = new Int(); +public final class IntType extends Type { + public static final IntType INSTANCE = new IntType(); private static final RestrictableGenerator GEN_INT = Generators.G.ints(); private static final List OPERATIONS = List.of( - new Operation.Unary("(-(", Int.INSTANCE, "))"), - new Operation.Unary("(~", Int.INSTANCE, ")"), + new Operation.Unary("(-(", IntType.INSTANCE, "))"), + new Operation.Unary("(~", IntType.INSTANCE, ")"), - new Operation.Binary("(", Int.INSTANCE, " + ", Int.INSTANCE, ")"), - new Operation.Binary("(", Int.INSTANCE, " - ", Int.INSTANCE, ")"), - new Operation.Binary("(", Int.INSTANCE, " * ", Int.INSTANCE, ")"), - new Operation.Binary("(", Int.INSTANCE, " / ", Int.INSTANCE, ")"), - new Operation.Binary("(", Int.INSTANCE, " % ", Int.INSTANCE, ")"), - new Operation.Binary("(", Int.INSTANCE, " & ", Int.INSTANCE, ")"), - new Operation.Binary("(", Int.INSTANCE, " | ", Int.INSTANCE, ")"), - new Operation.Binary("(", Int.INSTANCE, " ^ ", Int.INSTANCE, ")"), - new Operation.Binary("(", Int.INSTANCE, " << ", Int.INSTANCE, ")"), - new Operation.Binary("(", Int.INSTANCE, " >> ", Int.INSTANCE, ")"), - new Operation.Binary("(", Int.INSTANCE, " >>> ", Int.INSTANCE, ")") + new Operation.Binary("(", IntType.INSTANCE, " + ", IntType.INSTANCE, ")"), + new Operation.Binary("(", IntType.INSTANCE, " - ", IntType.INSTANCE, ")"), + new Operation.Binary("(", IntType.INSTANCE, " * ", IntType.INSTANCE, ")"), + new Operation.Binary("(", IntType.INSTANCE, " / ", IntType.INSTANCE, ")"), + new Operation.Binary("(", IntType.INSTANCE, " % ", IntType.INSTANCE, ")"), + new Operation.Binary("(", IntType.INSTANCE, " & ", IntType.INSTANCE, ")"), + new Operation.Binary("(", IntType.INSTANCE, " | ", IntType.INSTANCE, ")"), + new Operation.Binary("(", IntType.INSTANCE, " ^ ", IntType.INSTANCE, ")"), + new Operation.Binary("(", IntType.INSTANCE, " << ", IntType.INSTANCE, ")"), + new Operation.Binary("(", IntType.INSTANCE, " >> ", IntType.INSTANCE, ")"), + new Operation.Binary("(", IntType.INSTANCE, " >>> ", IntType.INSTANCE, ")"), - // TODO: - // new TernaryOperator("(", ExpressionType.BOOLEAN, " ? ", ExpressionType.INT, " : ", ExpressionType.INT, ")") + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", IntType.INSTANCE, " : ", IntType.INSTANCE, ")") ); @Override diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java new file mode 100644 index 0000000000000..d6b260ffd56bf --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.template_library.types; + +import java.util.List; + +import compiler.lib.generators.Generators; +import compiler.lib.generators.RestrictableGenerator; + +import compiler.lib.template_library.Type; +import compiler.lib.template_library.Operation; + +public final class LongType extends Type { + public static final LongType INSTANCE = new LongType(); + private static final RestrictableGenerator GEN_LONG = Generators.G.longs(); + + private static final List OPERATIONS = List.of( + new Operation.Unary("(-(", LongType.INSTANCE, "))"), + new Operation.Unary("(~", LongType.INSTANCE, ")"), + + new Operation.Binary("(", LongType.INSTANCE, " + ", LongType.INSTANCE, ")"), + new Operation.Binary("(", LongType.INSTANCE, " - ", LongType.INSTANCE, ")"), + new Operation.Binary("(", LongType.INSTANCE, " * ", LongType.INSTANCE, ")"), + new Operation.Binary("(", LongType.INSTANCE, " / ", LongType.INSTANCE, ")"), + new Operation.Binary("(", LongType.INSTANCE, " % ", LongType.INSTANCE, ")"), + new Operation.Binary("(", LongType.INSTANCE, " & ", LongType.INSTANCE, ")"), + new Operation.Binary("(", LongType.INSTANCE, " | ", LongType.INSTANCE, ")"), + new Operation.Binary("(", LongType.INSTANCE, " ^ ", LongType.INSTANCE, ")"), + new Operation.Binary("(", LongType.INSTANCE, " << ", LongType.INSTANCE, ")"), + new Operation.Binary("(", LongType.INSTANCE, " >> ", LongType.INSTANCE, ")"), + new Operation.Binary("(", LongType.INSTANCE, " >>> ", LongType.INSTANCE, ")"), + + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", LongType.INSTANCE, " : ", LongType.INSTANCE, ")") + ); + + @Override + public final String name() { return "long"; } + + @Override + public final Object con() { + return GEN_LONG.next(); + } + + @Override + public final List operations() { + return OPERATIONS; + } +} From 9630be7ae7956f1b6f9085279f8a9a71a3d08a6f Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 21 Feb 2025 09:42:27 +0100 Subject: [PATCH 104/212] both gold and reference version of test --- .../lib/template_library/Expression.java | 2 ++ .../compiler/lib/template_library/Types.java | 2 +- .../examples/TestFuzzExpression.java | 35 ++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index d0646d745e291..87d03f0c231be 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -54,4 +54,6 @@ public final TemplateWithArgs withArgs(List args) { // TODO: check length? return template.withArgs(args); } + + } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Types.java b/test/hotspot/jtreg/compiler/lib/template_library/Types.java index f13d55491a0a9..1239fda1fe372 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Types.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Types.java @@ -44,7 +44,7 @@ import static compiler.lib.template_framework.Template.sampleName; /** - * + * TODO: remove this file !!!!!!!! */ public abstract class Types { private static final Random RANDOM = Utils.getRandomInstance(); diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index b5481f784ea30..f326d0811ab51 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -93,6 +93,7 @@ public static String generate(CompileFramework comp) { return body( """ // --- $test start --- + // Using $reference // type: #type @DontCompile @@ -128,11 +129,43 @@ public static String generate(CompileFramework comp) { ); }); + var template2 = Template.make("type", (Type type)-> { + Expression exp = Expressions.expression(type, Type.primitives(), 2); + return body( + """ + // --- $test start --- + // Using $GOLD + // type: #type + + static final Object $GOLD = $test(); + + @Test + public static Object $test() { + try { + """, + " return ", exp.withArgs(List.of()), ";\n", + """ + } catch (Exception e) { + return e; + } + } + + @Check(test = "$test") + public static void $check(Object result) { + Verify.checkEQ(result, $GOLD); + } + + // --- $test end --- + """ + ); + }); + // Use template1 100 times with every type. List templates = new ArrayList<>(); - for (int i = 0; i < 100; i++) { + for (int i = 0; i < 50; i++) { for (Type type : Type.primitives()) { templates.add(template1.withArgs(type)); + templates.add(template2.withArgs(type)); } } return IRTestClass.TEMPLATE.withArgs(info, templates).render(); From 27f5c90ee051fbf16f74aa776d28771e04cb39d7 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 21 Feb 2025 10:16:00 +0100 Subject: [PATCH 105/212] implement expression arguments --- .../lib/template_library/Expression.java | 7 +++++-- .../lib/template_library/Expressions.java | 21 +++++++------------ .../examples/TestFuzzExpression.java | 7 ++++--- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index 87d03f0c231be..96045d38c8588 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -51,9 +51,12 @@ public final class Expression { public final List types() { return this.types; } public final TemplateWithArgs withArgs(List args) { - // TODO: check length? + if (args.size() != types.size()) { throw new RuntimeException("'args' must have the same size as 'types'"); } return template.withArgs(args); } - + public final TemplateWithArgs withRandomArgs() { + List args = types.stream().map(type -> type.con()).toList(); + return withArgs(args); + } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java index 9916f461805a1..18a17e674312c 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java @@ -69,14 +69,11 @@ public static final Expression expression(Type resultType, List allowedTyp List types = new ArrayList(); ExpressionGenerator generator = expressionGenerator(resultType, allowedTypesSet, maxDepth, types); - // TODO: - // Output: lambda(args) -> list of tokens, using args - // Step: lambda(args, tokens) -> update tokens var template = Template.make("args", (List args) -> body( generator.tokens(args) )); - return new Expression(template, List.of()); + return new Expression(template, types); } private static final ExpressionGenerator expressionGenerator(Type resultType, HashSet allowedTypes, int maxDepth, List types) { @@ -91,8 +88,13 @@ private static final ExpressionGenerator expressionGenerator(Type resultType, Ha private static final ExpressionGeneratorStep expressionGeneratorStep(Type resultType, HashSet allowedTypes, int maxDepth, List types) { List ops = resultType.operations().stream().filter(o -> o.hasOnlyTypes(allowedTypes)).toList(); if (maxDepth <= 0 || ops.isEmpty()) { - // TODO: add to types in some cases! - return expressionGeneratorStepCon(resultType); + // Remember which type we need to fill the ith argument with. + int i = types.size(); + types.add(resultType); + return (List tokens, List args) -> { + // Extract the ith argument. + tokens.add(args.get(i)); + }; } switch (choice(ops)) { case Operation.Unary(String s0, Type t0, String s1) -> { @@ -130,11 +132,4 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result } } } - - private static final ExpressionGeneratorStep expressionGeneratorStepCon(Type type) { - Object c = type.con(); - return (List tokens, List args) -> { - tokens.add(c); - }; - } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index f326d0811ab51..9edc9efcbc116 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -90,6 +90,7 @@ public static String generate(CompileFramework comp) { var template1 = Template.make("type", (Type type)-> { Expression exp = Expressions.expression(type, Type.primitives(), 2); + List args = exp.types().stream().map(t -> t.con()).toList(); return body( """ // --- $test start --- @@ -100,7 +101,7 @@ public static String generate(CompileFramework comp) { public static Object $reference() { try { """, - " return ", exp.withArgs(List.of()), ";\n", + " return ", exp.withArgs(args), ";\n", """ } catch (Exception e) { return e; @@ -111,7 +112,7 @@ public static String generate(CompileFramework comp) { public static Object $test() { try { """, - " return ", exp.withArgs(List.of()), ";\n", + " return ", exp.withArgs(args), ";\n", """ } catch (Exception e) { return e; @@ -143,7 +144,7 @@ public static String generate(CompileFramework comp) { public static Object $test() { try { """, - " return ", exp.withArgs(List.of()), ";\n", + " return ", exp.withRandomArgs(), ";\n", """ } catch (Exception e) { return e; From e79aeba37646105ceefed215b5d1748dccff896a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 21 Feb 2025 11:30:22 +0100 Subject: [PATCH 106/212] cleanup and ideas --- .../compiler/lib/template_library/Expression.java | 12 ++++++++++-- .../compiler/lib/template_library/Expressions.java | 8 -------- .../examples/TestFuzzExpression.java | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index 96045d38c8588..d724167e0b26b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -55,8 +55,16 @@ public final TemplateWithArgs withArgs(List args) { return template.withArgs(args); } + public final List randomArgs() { + return types.stream().map(type -> type.con()).toList(); + } + + // We would like to use identical args multiple times, but possible field / variable defs have to happen only once. + // So we need to separate possible generation for the arguments with the loads. + // So we have + // - def-tokens: define fields/vars, or do nothing if we reference something that already exists / con. + // - use-tokens: reference defined fields/vars, or just constant. public final TemplateWithArgs withRandomArgs() { - List args = types.stream().map(type -> type.con()).toList(); - return withArgs(args); + return withArgs(randomArgs()); } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java index 18a17e674312c..03330542cd92f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java @@ -40,7 +40,6 @@ import static compiler.lib.template_framework.Template.sampleName; public abstract class Expressions { - private static final Random RANDOM = Utils.getRandomInstance(); private static T choice(List list) { @@ -49,13 +48,6 @@ private static T choice(List list) { return list.get(i); } - public static final Expression constant(Type type) { - var template = Template.make("args", (List args) -> body( - type.con() - )); - return new Expression(template, List.of()); - } - private interface ExpressionGenerator { List tokens(List args); } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index 9edc9efcbc116..60d9c56a744e8 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -90,7 +90,7 @@ public static String generate(CompileFramework comp) { var template1 = Template.make("type", (Type type)-> { Expression exp = Expressions.expression(type, Type.primitives(), 2); - List args = exp.types().stream().map(t -> t.con()).toList(); + List args = exp.randomArgs(); return body( """ // --- $test start --- From 08b7e304c7940ea5aa37a397fa28be338dc4a0eb Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 21 Feb 2025 11:38:15 +0100 Subject: [PATCH 107/212] more cleanup --- .../lib/template_library/Expression.java | 7 ------- .../lib/template_library/Expressions.java | 19 +------------------ .../lib/template_library/Library.java | 12 ++++++++++++ 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index d724167e0b26b..9b9349613ff7f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -27,13 +27,6 @@ import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateWithArgs; -import static compiler.lib.template_framework.Template.body; -import static compiler.lib.template_framework.Template.$; -import static compiler.lib.template_framework.Template.fuel; -import static compiler.lib.template_framework.Template.setFuelCost; -import static compiler.lib.template_framework.Template.defineName; -import static compiler.lib.template_framework.Template.countNames; -import static compiler.lib.template_framework.Template.sampleName; /** * TODO: description diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java index 03330542cd92f..9e588329e34b4 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java @@ -26,28 +26,11 @@ import java.util.List; import java.util.ArrayList; import java.util.HashSet; -import java.util.Random; -import jdk.test.lib.Utils; import compiler.lib.template_framework.Template; -import compiler.lib.template_framework.TemplateWithArgs; import static compiler.lib.template_framework.Template.body; -import static compiler.lib.template_framework.Template.$; -import static compiler.lib.template_framework.Template.fuel; -import static compiler.lib.template_framework.Template.setFuelCost; -import static compiler.lib.template_framework.Template.defineName; -import static compiler.lib.template_framework.Template.countNames; -import static compiler.lib.template_framework.Template.sampleName; public abstract class Expressions { - private static final Random RANDOM = Utils.getRandomInstance(); - - private static T choice(List list) { - if (list.isEmpty()) { return null; } - int i = RANDOM.nextInt(list.size()); - return list.get(i); - } - private interface ExpressionGenerator { List tokens(List args); } @@ -88,7 +71,7 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result tokens.add(args.get(i)); }; } - switch (choice(ops)) { + switch (Library.choice(ops)) { case Operation.Unary(String s0, Type t0, String s1) -> { ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, allowedTypes, maxDepth-1, types); return (List tokens, List args) -> { diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Library.java b/test/hotspot/jtreg/compiler/lib/template_library/Library.java index 293408731aae2..b778efba3a727 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Library.java @@ -23,6 +23,10 @@ package compiler.lib.template_library; +import java.util.List; +import java.util.Random; +import jdk.test.lib.Utils; + import compiler.lib.template_framework.Hook; /** @@ -34,6 +38,14 @@ * TODO configure choice and other randomization? */ public abstract class Library { + private static final Random RANDOM = Utils.getRandomInstance(); + public static final Hook CLASS_HOOK = new Hook("Class"); public static final Hook METHOD_HOOK = new Hook("Method"); + + static T choice(List list) { + if (list.isEmpty()) { return null; } + int i = RANDOM.nextInt(list.size()); + return list.get(i); + } } From c76263f0a69b9cf2e642b941a2a870dee8c2617e Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 21 Feb 2025 11:43:07 +0100 Subject: [PATCH 108/212] expressions refactor --- .../lib/template_library/Expression.java | 79 +++++++++++++ .../lib/template_library/Expressions.java | 110 ------------------ .../examples/TestFuzzExpression.java | 5 +- 3 files changed, 81 insertions(+), 113 deletions(-) delete mode 100644 test/hotspot/jtreg/compiler/lib/template_library/Expressions.java diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index 9b9349613ff7f..cf01d71cb799f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -27,6 +27,7 @@ import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateWithArgs; +import static compiler.lib.template_framework.Template.body; /** * TODO: description @@ -60,4 +61,82 @@ public final List randomArgs() { public final TemplateWithArgs withRandomArgs() { return withArgs(randomArgs()); } + + private interface ExpressionGenerator { + List tokens(List args); + } + + private interface ExpressionGeneratorStep { + void addTokens(List tokens, List args); + } + + public static final Expression make(Type resultType, List allowedTypes, int maxDepth) { + HashSet allowedTypesSet = new HashSet(allowedTypes); + + List types = new ArrayList(); + ExpressionGenerator generator = expressionGenerator(resultType, allowedTypesSet, maxDepth, types); + + var template = Template.make("args", (List args) -> body( + generator.tokens(args) + )); + return new Expression(template, types); + } + + private static final ExpressionGenerator expressionGenerator(Type resultType, HashSet allowedTypes, int maxDepth, List types) { + ExpressionGeneratorStep step = expressionGeneratorStep(resultType, allowedTypes, maxDepth, types); + return (List args) -> { + List tokens = new ArrayList(); + step.addTokens(tokens, args); + return tokens; + }; + } + + private static final ExpressionGeneratorStep expressionGeneratorStep(Type resultType, HashSet allowedTypes, int maxDepth, List types) { + List ops = resultType.operations().stream().filter(o -> o.hasOnlyTypes(allowedTypes)).toList(); + if (maxDepth <= 0 || ops.isEmpty()) { + // Remember which type we need to fill the ith argument with. + int i = types.size(); + types.add(resultType); + return (List tokens, List args) -> { + // Extract the ith argument. + tokens.add(args.get(i)); + }; + } + switch (Library.choice(ops)) { + case Operation.Unary(String s0, Type t0, String s1) -> { + ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, allowedTypes, maxDepth-1, types); + return (List tokens, List args) -> { + tokens.add(s0); + step0.addTokens(tokens, args); + tokens.add(s1); + }; + } + case Operation.Binary(String s0, Type t0, String s1, Type t1, String s2) -> { + ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, allowedTypes, maxDepth-1, types); + ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, allowedTypes, maxDepth-1, types); + return (List tokens, List args) -> { + tokens.add(s0); + step0.addTokens(tokens, args); + tokens.add(s1); + step1.addTokens(tokens, args); + tokens.add(s2); + }; + } + case Operation.Ternary(String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3) -> { + ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, allowedTypes, maxDepth-1, types); + ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, allowedTypes, maxDepth-1, types); + ExpressionGeneratorStep step2 = expressionGeneratorStep(t1, allowedTypes, maxDepth-1, types); + return (List tokens, List args) -> { + tokens.add(s0); + step0.addTokens(tokens, args); + tokens.add(s1); + step1.addTokens(tokens, args); + tokens.add(s2); + step2.addTokens(tokens, args); + tokens.add(s3); + }; + } + } + } + } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java b/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java deleted file mode 100644 index 9e588329e34b4..0000000000000 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expressions.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package compiler.lib.template_library; - -import java.util.List; -import java.util.ArrayList; -import java.util.HashSet; - -import compiler.lib.template_framework.Template; -import static compiler.lib.template_framework.Template.body; - -public abstract class Expressions { - private interface ExpressionGenerator { - List tokens(List args); - } - - private interface ExpressionGeneratorStep { - void addTokens(List tokens, List args); - } - - public static final Expression expression(Type resultType, List allowedTypes, int maxDepth) { - HashSet allowedTypesSet = new HashSet(allowedTypes); - - List types = new ArrayList(); - ExpressionGenerator generator = expressionGenerator(resultType, allowedTypesSet, maxDepth, types); - - var template = Template.make("args", (List args) -> body( - generator.tokens(args) - )); - return new Expression(template, types); - } - - private static final ExpressionGenerator expressionGenerator(Type resultType, HashSet allowedTypes, int maxDepth, List types) { - ExpressionGeneratorStep step = expressionGeneratorStep(resultType, allowedTypes, maxDepth, types); - return (List args) -> { - List tokens = new ArrayList(); - step.addTokens(tokens, args); - return tokens; - }; - } - - private static final ExpressionGeneratorStep expressionGeneratorStep(Type resultType, HashSet allowedTypes, int maxDepth, List types) { - List ops = resultType.operations().stream().filter(o -> o.hasOnlyTypes(allowedTypes)).toList(); - if (maxDepth <= 0 || ops.isEmpty()) { - // Remember which type we need to fill the ith argument with. - int i = types.size(); - types.add(resultType); - return (List tokens, List args) -> { - // Extract the ith argument. - tokens.add(args.get(i)); - }; - } - switch (Library.choice(ops)) { - case Operation.Unary(String s0, Type t0, String s1) -> { - ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, allowedTypes, maxDepth-1, types); - return (List tokens, List args) -> { - tokens.add(s0); - step0.addTokens(tokens, args); - tokens.add(s1); - }; - } - case Operation.Binary(String s0, Type t0, String s1, Type t1, String s2) -> { - ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, allowedTypes, maxDepth-1, types); - ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, allowedTypes, maxDepth-1, types); - return (List tokens, List args) -> { - tokens.add(s0); - step0.addTokens(tokens, args); - tokens.add(s1); - step1.addTokens(tokens, args); - tokens.add(s2); - }; - } - case Operation.Ternary(String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3) -> { - ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, allowedTypes, maxDepth-1, types); - ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, allowedTypes, maxDepth-1, types); - ExpressionGeneratorStep step2 = expressionGeneratorStep(t1, allowedTypes, maxDepth-1, types); - return (List tokens, List args) -> { - tokens.add(s0); - step0.addTokens(tokens, args); - tokens.add(s1); - step1.addTokens(tokens, args); - tokens.add(s2); - step2.addTokens(tokens, args); - tokens.add(s3); - }; - } - } - } -} diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index 60d9c56a744e8..ff90746708eba 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -50,7 +50,6 @@ import compiler.lib.template_library.IRTestClass; import compiler.lib.template_library.Type; import compiler.lib.template_library.Expression; -import compiler.lib.template_library.Expressions; /** * This is a basic expression fuzzer: it generates random expressions using {@link Library.Expression}, @@ -89,7 +88,7 @@ public static String generate(CompileFramework comp) { "compiler.lib.verify.*")); var template1 = Template.make("type", (Type type)-> { - Expression exp = Expressions.expression(type, Type.primitives(), 2); + Expression exp = Expression.make(type, Type.primitives(), 2); List args = exp.randomArgs(); return body( """ @@ -131,7 +130,7 @@ public static String generate(CompileFramework comp) { }); var template2 = Template.make("type", (Type type)-> { - Expression exp = Expressions.expression(type, Type.primitives(), 2); + Expression exp = Expression.make(type, Type.primitives(), 2); return body( """ // --- $test start --- From adbafd71d520cd1e10b773f338cb1835041ad9fc Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 21 Feb 2025 12:18:42 +0100 Subject: [PATCH 109/212] stub Value.java --- .../lib/template_library/Expression.java | 18 +++++-- .../compiler/lib/template_library/Value.java | 47 +++++++++++++++++++ .../examples/TestFuzzExpression.java | 16 ++++--- 3 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/Value.java diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index cf01d71cb799f..37dfda641ce84 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -23,11 +23,14 @@ package compiler.lib.template_library; +import java.util.ArrayList; import java.util.List; +import java.util.HashSet; import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateWithArgs; import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.setFuelCost; /** * TODO: description @@ -49,8 +52,8 @@ public final TemplateWithArgs withArgs(List args) { return template.withArgs(args); } - public final List randomArgs() { - return types.stream().map(type -> type.con()).toList(); + public final List randomArgValues() { + return types.stream().map(Value::makeRandom).toList(); } // We would like to use identical args multiple times, but possible field / variable defs have to happen only once. @@ -59,7 +62,15 @@ public final List randomArgs() { // - def-tokens: define fields/vars, or do nothing if we reference something that already exists / con. // - use-tokens: reference defined fields/vars, or just constant. public final TemplateWithArgs withRandomArgs() { - return withArgs(randomArgs()); + List argValues = randomArgValues(); + List def = argValues.stream().map(v -> v.defTokens()).toList(); + List use = argValues.stream().map(v -> v.useTokens()).toList(); + var template = Template.make(() -> body( + setFuelCost(0), + def, + withArgs(use) + )); + return template.withArgs(); } private interface ExpressionGenerator { @@ -138,5 +149,4 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result } } } - } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Value.java b/test/hotspot/jtreg/compiler/lib/template_library/Value.java new file mode 100644 index 0000000000000..d740d4beeed98 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/Value.java @@ -0,0 +1,47 @@ +/* + * 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_library; + +import java.util.ArrayList; +import java.util.List; +import java.util.HashSet; + +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateWithArgs; +import static compiler.lib.template_framework.Template.body; + +/** + * TODO: description + */ +public record Value(Object defTokens, Object useTokens) { + + public static Value fromUseToken(Object useToken) { + return new Value("", useToken); + } + + public static Value makeRandom(Type type) { + // TODO: more cases + return fromUseToken(type.con()); + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index ff90746708eba..df7afb6861a5c 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -50,6 +50,7 @@ import compiler.lib.template_library.IRTestClass; import compiler.lib.template_library.Type; import compiler.lib.template_library.Expression; +import compiler.lib.template_library.Value; /** * This is a basic expression fuzzer: it generates random expressions using {@link Library.Expression}, @@ -88,9 +89,12 @@ public static String generate(CompileFramework comp) { "compiler.lib.verify.*")); var template1 = Template.make("type", (Type type)-> { - Expression exp = Expression.make(type, Type.primitives(), 2); - List args = exp.randomArgs(); + Expression expression = Expression.make(type, Type.primitives(), 2); + List argValues = expression.randomArgValues(); + List def = argValues.stream().map(v -> v.defTokens()).toList(); + List use = argValues.stream().map(v -> v.useTokens()).toList(); return body( + def, """ // --- $test start --- // Using $reference @@ -100,7 +104,7 @@ public static String generate(CompileFramework comp) { public static Object $reference() { try { """, - " return ", exp.withArgs(args), ";\n", + " return ", expression.withArgs(use), ";\n", """ } catch (Exception e) { return e; @@ -111,7 +115,7 @@ public static String generate(CompileFramework comp) { public static Object $test() { try { """, - " return ", exp.withArgs(args), ";\n", + " return ", expression.withArgs(use), ";\n", """ } catch (Exception e) { return e; @@ -130,7 +134,7 @@ public static String generate(CompileFramework comp) { }); var template2 = Template.make("type", (Type type)-> { - Expression exp = Expression.make(type, Type.primitives(), 2); + Expression expression = Expression.make(type, Type.primitives(), 2); return body( """ // --- $test start --- @@ -143,7 +147,7 @@ public static String generate(CompileFramework comp) { public static Object $test() { try { """, - " return ", exp.withRandomArgs(), ";\n", + " return ", expression.withRandomArgs(), ";\n", """ } catch (Exception e) { return e; From 2baec92e655d9b0e06656c4f456465cb628ffb1a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 21 Feb 2025 17:17:32 +0100 Subject: [PATCH 110/212] refactor names --- ...DefineNameToken.java => AddNameToken.java} | 2 +- .../lib/template_framework/CodeFrame.java | 47 ++---- .../compiler/lib/template_framework/Name.java | 43 +++++ .../lib/template_framework/NameSet.java | 61 ++++--- .../lib/template_framework/Renderer.java | 10 +- .../lib/template_framework/Template.java | 26 ++- .../lib/template_framework/Token.java | 2 +- .../tests/TestTemplate.java | 153 ++++++++++-------- 8 files changed, 188 insertions(+), 156 deletions(-) rename test/hotspot/jtreg/compiler/lib/template_framework/{DefineNameToken.java => AddNameToken.java} (92%) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/Name.java diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/DefineNameToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/AddNameToken.java similarity index 92% rename from test/hotspot/jtreg/compiler/lib/template_framework/DefineNameToken.java rename to test/hotspot/jtreg/compiler/lib/template_framework/AddNameToken.java index b36f76d4e2eb6..4f1f7e569bf0f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/DefineNameToken.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/AddNameToken.java @@ -23,4 +23,4 @@ package compiler.lib.template_framework; -record DefineNameToken(String name, Object type, boolean mutable) implements Token {} +record AddNameToken(Name name) implements Token {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java index ce7979e4e1440..0aecbe4ebbb18 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java @@ -53,27 +53,21 @@ class CodeFrame { private final Map hookCodeLists = new HashMap<>(); /** - * The {@link NameSet}s are used for variable and fields etc. There is one set that - * contains only the mutable names, and another that contains all. This sampling only - * mutable names, or sampling from all names including immutable names. + * The {@link NameSet} is used for variable and fields etc. */ - final NameSet mutableNames; - final NameSet allNames; + final NameSet names; private CodeFrame(CodeFrame parent, boolean isTransparentForNames) { this.parent = parent; if (parent == null) { - // NameSets without any parent. - this.mutableNames = new NameSet(null); - this.allNames = new NameSet(null); + // NameSet without any parent. + this.names = new NameSet(null); } else if (isTransparentForNames) { - // We use the same NameSets as the parent - makes it transparent. - this.mutableNames = parent.mutableNames; - this.allNames = parent.allNames; + // We use the same NameSet as the parent - makes it transparent. + this.names = parent.names; } else { - // New NameSets, to make sure we have a nested scope for the names. - this.mutableNames = new NameSet(parent.mutableNames); - this.allNames = new NameSet(parent.allNames); + // New NameSet, to make sure we have a nested scope for the names. + this.names = new NameSet(parent.names); } } @@ -125,29 +119,16 @@ boolean hasHook(Hook hook) { return hookCodeLists.containsKey(hook); } - private NameSet nameSet(boolean onlyMutable) { - if (onlyMutable) { - return mutableNames; - } else { - return allNames; - } - } - - void defineName(String name, Object type, boolean mutable) { - if (mutable) { - mutableNames.add(name, type); - allNames.add(name, type); - } else { - allNames.add(name, type); - } + void addName(Name name) { + names.add(name); } - int countNames(Object type, boolean onlyMutable) { - return nameSet(onlyMutable).count(type); + long weighNames(Name.Type type, boolean onlyMutable) { + return names.weight(type, onlyMutable); } - String sampleName(Object type, boolean onlyMutable) { - return nameSet(onlyMutable).sample(type); + Name sampleName(Name.Type type, boolean onlyMutable) { + return names.sample(type, onlyMutable); } Code getCode() { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Name.java b/test/hotspot/jtreg/compiler/lib/template_framework/Name.java new file mode 100644 index 0000000000000..b6d0e9052ec11 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Name.java @@ -0,0 +1,43 @@ +/* + * 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; + +import java.util.HashMap; +import java.util.Map; +import java.util.ArrayList; +import java.util.List; + +public record Name(String name, Name.Type type, boolean mutable, int weight) { + + public Name { + if (0 >= weight || weight > 1000) { + throw new IllegalArgumentException("Unexpected weight: " + weight); + } + } + + public interface Type { + String name(); + boolean isSubtypeOf(Type other); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java b/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java index 77cca99d938de..3c49e362cd794 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java @@ -23,8 +23,6 @@ package compiler.lib.template_framework; -import java.util.HashMap; -import java.util.Map; import java.util.ArrayList; import java.util.List; import java.util.Random; @@ -32,63 +30,62 @@ import jdk.test.lib.Utils; /** - * The {@link NameSet} defines a set of names (e.g. fields or variable names). They extend the + * The {@link NameSet} defines a set of {@link Name}s (e.g. fields or variable names). They extend the * set of the {@code 'parent'} set. */ class NameSet { static final Random RANDOM = Utils.getRandomInstance(); private final NameSet parent; - private final Map> names = new HashMap<>(); + private final List names = new ArrayList<>(); NameSet(NameSet parent) { this.parent = parent; } - public int countLocal(Object type) { - List locals = names.get(type); - return (locals == null) ? 0 : locals.size(); + private long localWeight(Name.Type type, boolean onlyMutable) { + long sum = 0; + for (var name : names) { + if (name.type().isSubtypeOf(type) && (name.mutable() || !onlyMutable)) { + sum += name.weight(); + } + } + return sum; } - public int count(Object type) { - int c = countLocal(type); - if (parent != null) { c += parent.count(type); } - return c; + public long weight(Name.Type type, boolean onlyMutable) { + long w = localWeight(type, onlyMutable); + if (parent != null) { w += parent.weight(type, onlyMutable); } + return w; } /** * Randomly sample a name from this set or a parent set, restricted to the specified type. */ - public String sample(Object type) { - int c = count(type); - if (c == 0) { + public Name sample(Name.Type type, boolean onlyMutable) { + long w = weight(type, onlyMutable); + if (w <= 0) { throw new RendererException("No variable of type '" + type.toString() + "'."); } - // Maybe sample from parent. - if (parent != null) { - int pc = parent.count(type); - int r = RANDOM.nextInt(c); - if (r < pc) { - return parent.sample(type); + long r = RANDOM.nextLong(w); + return sample(type, onlyMutable, r); + } + + private Name sample(Name.Type type, boolean onlyMutable, long r) { + for (var name : names) { + if (name.type().isSubtypeOf(type) && (name.mutable() || !onlyMutable)) { + r -= name.weight(); + if (r < 0) { return name; } } } - - List locals = names.get(type); - int r = RANDOM.nextInt(locals.size()); - return locals.get(r); + return parent.sample(type, onlyMutable, r); } /** * Add a variable of a specified type to the set. */ - public void add(String name, Object type) { - // Fetch list of variables - if non-existant create a new one. - List locals = names.get(type); - if (locals == null) { - locals = new ArrayList(); - names.put(type, locals); - } - locals.add(name); + public void add(Name name) { + names.add(name); } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 423b9d4afe4eb..d8411a58fdd5a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -127,11 +127,11 @@ void setFuelCost(float fuelCost) { currentTemplateFrame.setFuelCost(fuelCost); } - int countNames(Object type, boolean onlyMutable) { - return currentCodeFrame.countNames(type, onlyMutable); + long weighNames(Name.Type type, boolean onlyMutable) { + return currentCodeFrame.weighNames(type, onlyMutable); } - String sampleName(Object type, boolean onlyMutable) { + Name sampleName(Name.Type type, boolean onlyMutable) { return currentCodeFrame.sampleName(type, onlyMutable); } @@ -260,8 +260,8 @@ case HookInsertToken(Hook hook, TemplateWithArgs t) -> { callerCodeFrame.addCode(currentCodeFrame.getCode()); currentCodeFrame = callerCodeFrame; } - case DefineNameToken(String name, Object type, boolean mutable) -> { - currentCodeFrame.defineName(name, type, mutable); + case AddNameToken(Name name) -> { + currentCodeFrame.addName(name); } } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 8f623c40b5b32..0bcb82210f840 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -383,29 +383,27 @@ static Token setFuelCost(float fuelCost) { } /** - * Define a name in the current code frame. + * Add a {@link Name} in the current code frame. * Note that there can be duplicate definitions, and they simply increase - * the {@link countNames} count, and increase the probability of sampling + * the {@link weighNames} weight, and increase the probability of sampling * the name with {@link sampleName}. * - * @param name The {@code 'name'} of the name. - * @param type The type of the name. - * @param mutable Determines if the name is mutable or immutable. + * @param name The {@link Name} to be added to the current code frame. * @return The token that performs the defining action. */ - static Token defineName(String name, Object type, boolean mutable) { - return new DefineNameToken(name, type, mutable); + static Token addName(Name name) { + return new AddNameToken(name); } /** - * Count the number of names defined for the specified type. + * Weight the {@link Name}s for the specified {@link Name.Type}. * - * @param type The type of the names to be counted. - * @param onlyMutable Determines if we count the mutable names or all. - * @return The number of names for the specified parameters. + * @param type The type of the names to weigh. + * @param onlyMutable Determines if we weigh the mutable names or all. + * @return The weight of names for the specified parameters. */ - static int countNames(Object type, boolean onlyMutable) { - return Renderer.getCurrent().countNames(type, onlyMutable); + static long weighNames(Name.Type type, boolean onlyMutable) { + return Renderer.getCurrent().weighNames(type, onlyMutable); } /** @@ -415,7 +413,7 @@ static int countNames(Object type, boolean onlyMutable) { * @param onlyMutable Determines if we sample from the mutable names or all. * @return The sampled name. */ - static String sampleName(Object type, boolean onlyMutable) { + static Name sampleName(Name.Type type, boolean onlyMutable) { return Renderer.getCurrent().sampleName(type, onlyMutable); } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java index cc35c6d6a3a3b..4ea1b0eeea8b3 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java @@ -40,7 +40,7 @@ sealed interface Token permits StringToken, TemplateWithArgs.TwoArgsUse, HookSetToken, HookInsertToken, - DefineNameToken, + AddNameToken, NothingToken { static List parse(Object[] objects) { diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index c70a10b63dbbc..c7cd5181e4c52 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -38,6 +38,7 @@ import java.util.HashSet; import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.Name; import compiler.lib.template_framework.Hook; import compiler.lib.template_framework.TemplateBinding; import compiler.lib.template_framework.RendererException; @@ -46,8 +47,8 @@ import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.fuel; import static compiler.lib.template_framework.Template.setFuelCost; -import static compiler.lib.template_framework.Template.defineName; -import static compiler.lib.template_framework.Template.countNames; +import static compiler.lib.template_framework.Template.addName; +import static compiler.lib.template_framework.Template.weighNames; import static compiler.lib.template_framework.Template.sampleName; public class TestTemplate { @@ -55,6 +56,18 @@ interface FailingTest { void run(); } + private record MyPrimitive(String name) implements Name.Type { + @Override + public boolean isSubtypeOf(Name.Type other) { + return other instanceof MyPrimitive(String n) && n == name(); + } + + @Override + public String toString() { return name(); } + } + private static final MyPrimitive myInt = new MyPrimitive("int"); + private static final MyPrimitive myLong = new MyPrimitive("long"); + public static void main(String[] args) { testSingleLine(); testMultiLine(); @@ -82,8 +95,8 @@ public static void main(String[] args) { expectRendererException(() -> let("x","y"), "A Template method such as"); expectRendererException(() -> fuel(), "A Template method such as"); expectRendererException(() -> setFuelCost(1.0f), "A Template method such as"); - expectRendererException(() -> countNames("int", true), "A Template method such as"); - expectRendererException(() -> sampleName("int", true), "A Template method such as"); + expectRendererException(() -> weighNames(myInt, true), "A Template method such as"); + expectRendererException(() -> sampleName(myInt, true), "A Template method such as"); expectRendererException(() -> testFailingHook(), "Hook 'Hook1' was referenced but not found!"); expectRendererException(() -> testFailingSample(), "No variable of type 'int'."); expectRendererException(() -> testFailingHashtag1(), "Duplicate hashtag replacement for #a"); @@ -805,18 +818,18 @@ public static void testNames() { var hook1 = new Hook("Hook1"); var template1 = Template.make(() -> body( - "[", countNames("int", true), "]\n" + "[", weighNames(myInt, true), "]\n" )); - var template2 = Template.make("name", "type", (String name, Object type) -> body( - defineName(name, type, true), + var template2 = Template.make("name", "type", (String name, Name.Type type) -> body( + addName(new Name(name, type, true, 1)), "define #type #name\n", template1.withArgs() )); var template3 = Template.make(() -> body( "<\n", - hook1.insert(template2.withArgs($("name"), "int")), + hook1.insert(template2.withArgs($("name"), myInt)), "$name = 5\n", ">\n" )); @@ -831,7 +844,7 @@ public static void testNames() { "more\n", template1.withArgs(), "more\n", - template2.withArgs($("name"), "int"), + template2.withArgs($("name"), myInt), "more\n", template1.withArgs() ), @@ -843,22 +856,22 @@ public static void testNames() { String expected = """ { - [0] + [0L] define int name_4 - [1] - [0] + [1L] + [0L] something < name_4 = 5 > more - [1] + [1L] more define int name_1 - [2] + [2L] more - [1] - [0] + [1L] + [0L] } """; checkEQ(code, expected); @@ -867,46 +880,46 @@ public static void testNames() { public static void testNames2() { var hook1 = new Hook("Hook1"); - var template1 = Template.make("type", (Object type) -> body( - "[#type: ", countNames(type, true), " and ", countNames(type, false), "]\n" + var template1 = Template.make("type", (Name.Type type) -> body( + "[#type: ", weighNames(type, true), " and ", weighNames(type, false), "]\n" )); - var template2 = Template.make("name", "type", (String name, Object type) -> body( - defineName(name, type, true), + var template2 = Template.make("name", "type", (String name, Name.Type type) -> body( + addName(new Name(name, type, true, 1)), "define mutable #type #name\n", template1.withArgs(type) )); - var template3 = Template.make("name", "type", (String name, Object type) -> body( - defineName(name, type, false), + var template3 = Template.make("name", "type", (String name, Name.Type type) -> body( + addName(new Name(name, type, false, 1)), "define immutable #type #name\n", template1.withArgs(type) )); - var template4 = Template.make("type", (Object type) -> body( + var template4 = Template.make("type", (Name.Type type) -> body( "{ $store\n", hook1.insert(template2.withArgs($("name"), type)), "$name = 5\n", "} $store\n" )); - var template5 = Template.make("type", (Object type) -> body( + var template5 = Template.make("type", (Name.Type type) -> body( "{ $load\n", hook1.insert(template3.withArgs($("name"), type)), "blackhole($name)\n", "} $load\n" )); - var template6 = Template.make("type", (Object type) -> body( - let("v", sampleName(type, true)), + var template6 = Template.make("type", (Name.Type type) -> body( + let("v", sampleName(type, true).name()), "{ $sample\n", "#v = 7\n", "} $sample\n" )); - var template7 = Template.make("type", (Object type) -> body( - let("v", sampleName(type, false)), + var template7 = Template.make("type", (Name.Type type) -> body( + let("v", sampleName(type, false).name()), "{ $sample\n", "blackhole(#v)\n", "} $sample\n" @@ -914,22 +927,22 @@ public static void testNames2() { var template8 = Template.make(() -> body( "class $X {\n", - template1.withArgs("int"), + template1.withArgs(myInt), hook1.set( "begin $body\n", - template1.withArgs("int"), + template1.withArgs(myInt), "start with immutable\n", - template5.withArgs("int"), + template5.withArgs(myInt), "then load from it\n", - template7.withArgs("int"), - template1.withArgs("int"), + template7.withArgs(myInt), + template1.withArgs(myInt), "now make something mutable\n", - template4.withArgs("int"), + template4.withArgs(myInt), "then store to it\n", - template6.withArgs("int"), - template1.withArgs("int") + template6.withArgs(myInt), + template1.withArgs(myInt) ), - template1.withArgs("int"), + template1.withArgs(myInt), "}\n" )); @@ -937,13 +950,13 @@ public static void testNames2() { String expected = """ class X_1 { - [int: 0 and 0] + [int: 0L and 0L] define immutable int name_4 - [int: 0 and 1] + [int: 0L and 1L] define mutable int name_9 - [int: 1 and 2] + [int: 1L and 2L] begin body_1 - [int: 0 and 0] + [int: 0L and 0L] start with immutable { load_4 blackhole(name_4) @@ -952,7 +965,7 @@ class X_1 { { sample_7 blackhole(name_4) } sample_7 - [int: 0 and 1] + [int: 0L and 1L] now make something mutable { store_9 name_9 = 5 @@ -961,8 +974,8 @@ class X_1 { { sample_12 name_9 = 7 } sample_12 - [int: 1 and 2] - [int: 0 and 0] + [int: 1L and 2L] + [int: 0L and 0L] } """; checkEQ(code, expected); @@ -971,26 +984,26 @@ class X_1 { public static void testNames3() { var hook1 = new Hook("Hook1"); - var template1 = Template.make("type", (Object type) -> body( - "[#type: ", countNames(type, true), " and ", countNames(type, false), "]\n" + var template1 = Template.make("type", (Name.Type type) -> body( + "[#type: ", weighNames(type, true), " and ", weighNames(type, false), "]\n" )); - // Example that shows that defineName runs before any code gets generated. - // To avoid this behaviour, you have to wrap the defineName in their own template. + // Example that shows that addName runs before any code gets generated. + // To avoid this behaviour, you have to wrap the addName in their own template. var template2 = Template.make(() -> body( "class $Y {\n", - template1.withArgs("int"), + template1.withArgs(myInt), hook1.set( "begin $body\n", - template1.withArgs("int"), + template1.withArgs(myInt), "define mutable\n", - defineName($("v1"), "int", true), - template1.withArgs("int"), + addName(new Name($("v1"), myInt, true, 1)), + template1.withArgs(myInt), "define immutable\n", - defineName($("v1"), "int", false), - template1.withArgs("int") + addName(new Name($("v1"), myInt, false, 1)), + template1.withArgs(myInt) ), - template1.withArgs("int"), + template1.withArgs(myInt), "}\n" )); @@ -998,20 +1011,20 @@ public static void testNames3() { String expected = """ class Y_1 { - [int: 0 and 0] + [int: 0L and 0L] begin body_1 - [int: 0 and 0] + [int: 0L and 0L] define mutable - [int: 1 and 1] + [int: 1L and 1L] define immutable - [int: 1 and 2] - [int: 0 and 0] + [int: 1L and 2L] + [int: 0L and 0L] } """; checkEQ(code, expected); } - record MyItem(Object type, String op) {} + record MyItem(Name.Type type, String op) {} public static void testListArgument() { var template1 = Template.make("item", (MyItem item) -> body( @@ -1027,14 +1040,14 @@ public static void testListArgument() { "}\n" )); - List list = List.of(new MyItem("int", "+"), - new MyItem("int", "-"), - new MyItem("int", "*"), - new MyItem("int", "/"), - new MyItem("long", "+"), - new MyItem("long", "-"), - new MyItem("long", "*"), - new MyItem("long", "/")); + List list = List.of(new MyItem(myInt, "+"), + new MyItem(myInt, "-"), + new MyItem(myInt, "*"), + new MyItem(myInt, "/"), + new MyItem(myLong, "+"), + new MyItem(myLong, "-"), + new MyItem(myLong, "*"), + new MyItem(myLong, "/")); String code = template2.withArgs(list).render(); String expected = @@ -1087,7 +1100,7 @@ public static void testFailingHook() { public static void testFailingSample() { var template1 = Template.make(() -> body( - let("v", sampleName("int", true)), + let("v", sampleName(myInt, true).name()), "v is #v\n" )); From cef9f8d9eb106944194ee1fac814b1ac4af689eb Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 21 Feb 2025 17:39:59 +0100 Subject: [PATCH 111/212] improve documentation --- .../compiler/lib/template_framework/Name.java | 30 +++++++++++++++++++ .../lib/template_framework/Template.java | 6 +++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Name.java b/test/hotspot/jtreg/compiler/lib/template_framework/Name.java index b6d0e9052ec11..b9a0424062f69 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Name.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Name.java @@ -28,16 +28,46 @@ import java.util.ArrayList; import java.util.List; +/** + * {@link Name}s represent things like fields and local variables, or even method names that can be + * added to a code scope with {@link Template#addName} and sampled with {@link Template#sampleName}, + * according to the {@code 'weight'} of each {@link Name}. Every {@link Name} has a {@link Name.Type}, + * so that sampling can be restricted to these types, or subtypes, defined by {@link Name.Type#isSubtypeOf}. + * + * @param name The {@link String} name used in code. + * @param type The type with which we restrict {@link Template#weighNames} and {@link Template#sampleName}. + * @param mutable Defines if the name is considered mutable or immutable. + * @param weight The weight measured by {@link Template#weighNames} and according to which we sample with {@link Template#sampleName}. + */ public record Name(String name, Name.Type type, boolean mutable, int weight) { + /** + * Creates a new {@link Name}. + */ public Name { if (0 >= weight || weight > 1000) { throw new IllegalArgumentException("Unexpected weight: " + weight); } } + /** + * The interface for the type of a {@link Name}. + */ public interface Type { + /** + * The name of the type, that can be used in code. + * + * @return The {@link String} representation of the type, that can be used in code. + */ String name(); + + /** + * Defines the subtype relationship with other types, which is used to filter {@link Name}s + * in {@link Template#weighNames} and {@link Template#sampleName}. + * + * @param other The other type, where we check if it is the supertype of {@code 'this'}. + * @return If {@code 'this'} is a subtype of {@code 'other'}. + */ boolean isSubtypeOf(Type other); } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 0bcb82210f840..da35fb8d88e6e 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -68,7 +68,11 @@ *

* Code generation often involves defining fields and variables, which are then available inside a defined * scope, and can be sampled in any nested scope. To allow the use of names for multiple applications (e.g. - * fields, variables, methods, etc) ... TODO + * fields, variables, methods, etc), we define a {@link Name}, which captures the {@link String} representation + * to be used in code, as well as its type and if it is mutable. One can add such a {@link Name} to the + * current code scope with {@link addName}, and sample from the current or outer scopes with {@link sampleName}. + * When generating code, one might want to create {@link Name}s (variables, fields, etc) in local scope, or + * in some outer scope with the use of {@link Hook}s. */ public interface Template { From 4c0c462efcfe8abb32d3f717ef467887b33e72d5 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 24 Feb 2025 08:50:09 +0100 Subject: [PATCH 112/212] fix TestTutorial.java with Names --- .../examples/TestTutorial.java | 58 ++++++++++++------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java index 87c2bb79073b1..a3c9ab32d039c 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java @@ -40,13 +40,14 @@ import compiler.lib.template_framework.Template; import compiler.lib.template_framework.Hook; import compiler.lib.template_framework.TemplateBinding; +import compiler.lib.template_framework.Name; import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; import static compiler.lib.template_framework.Template.fuel; -import static compiler.lib.template_framework.Template.defineName; +import static compiler.lib.template_framework.Template.addName; import static compiler.lib.template_framework.Template.sampleName; -import static compiler.lib.template_framework.Template.countNames; +import static compiler.lib.template_framework.Template.weighNames; import compiler.lib.template_library.Library; @@ -402,33 +403,49 @@ public static void main() { return templateClass.withArgs().render(); } - // Example with names, i.e. defineName, countNames, and sampleName. - // These can be used to define and sample variables from outer scopes. + // In the example below ("generateWithNames"), we see the use of Names to add + // variables and fields to code scopes, and then sample from those variables + // and fields. Every Name has a Name.Type, which we now define for int and long. + private record MyPrimitive(String name) implements Name.Type { + @Override + public boolean isSubtypeOf(Name.Type other) { + return other instanceof MyPrimitive(String n) && n == name(); + } + + @Override + public String toString() { return name(); } + } + private static final MyPrimitive myInt = new MyPrimitive("int"); + private static final MyPrimitive myLong = new MyPrimitive("long"); + + // Example with names, i.e. addName, weighNames, and sampleName. + // These can be used to add variables and fields to code scopes, and then sample + // from the available variables and fields later. public static String generateWithNames() { - var templateSample = Template.make("type", (Object type) -> body( - let("name", sampleName(type, false)), + var templateSample = Template.make("type", (Name.Type type) -> body( + let("name", sampleName(type, false).name()), """ System.out.println("Sampling type #type: #name = " + #name); """ )); - var templateStaticField = Template.make("type", (Object type) -> body( - defineName($("field"), type, true), + var templateStaticField = Template.make("type", (Name.Type type) -> body( + addName(new Name($("field"), type, true, 1)), """ public static #type $field = 0; """ )); - var templateLocalVariable = Template.make("type", (Object type) -> body( - defineName($("var"), type, true), + var templateLocalVariable = Template.make("type", (Name.Type type) -> body( + addName(new Name($("var"), type, true, 1)), """ #type $var = 0; """ )); var templateStatus = Template.make(() -> body( - let("ints", countNames("int", false)), - let("longs", countNames("long", false)), + let("ints", weighNames(myInt, false)), + let("longs", weighNames(myLong, false)), """ System.out.println("Status: #ints ints, #longs longs."); """ @@ -439,14 +456,14 @@ public static String generateWithNames() { System.out.println("Starting inside main..."); """, templateStatus.withArgs(), - Library.METHOD_HOOK.insert(templateLocalVariable.withArgs("int")), - Library.METHOD_HOOK.insert(templateLocalVariable.withArgs("long")), - Library.CLASS_HOOK.insert(templateStaticField.withArgs("int")), - Library.CLASS_HOOK.insert(templateStaticField.withArgs("long")), + Library.METHOD_HOOK.insert(templateLocalVariable.withArgs(myInt)), + Library.METHOD_HOOK.insert(templateLocalVariable.withArgs(myLong)), + Library.CLASS_HOOK.insert(templateStaticField.withArgs(myInt)), + Library.CLASS_HOOK.insert(templateStaticField.withArgs(myLong)), templateStatus.withArgs(), // We should see a mix if fields and variables sampled. - Collections.nCopies(5, templateSample.withArgs("int")), - Collections.nCopies(5, templateSample.withArgs("long")), + Collections.nCopies(5, templateSample.withArgs(myInt)), + Collections.nCopies(5, templateSample.withArgs(myLong)), templateStatus.withArgs(), """ System.out.println("Finishing inside main."); @@ -459,8 +476,8 @@ public static String generateWithNames() { """, templateStatus.withArgs(), // We still have all the field definitions from main. - Collections.nCopies(5, templateSample.withArgs("int")), - Collections.nCopies(5, templateSample.withArgs("long")), + Collections.nCopies(5, templateSample.withArgs(myInt)), + Collections.nCopies(5, templateSample.withArgs(myLong)), templateStatus.withArgs(), """ System.out.println("Finishing inside other."); @@ -518,5 +535,4 @@ private static void other() { // Render templateClass to String. return templateClass.withArgs().render(); } - } From a80d41429963bb8c9cf4c4058d71fb036937010a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 24 Feb 2025 09:46:23 +0100 Subject: [PATCH 113/212] PrimitiveType refactor --- .../compiler/lib/template_library/Type.java | 4 ++- .../compiler/lib/template_library/Value.java | 15 ++++++++ .../template_library/types/BooleanType.java | 3 +- .../template_library/types/DoubleType.java | 3 +- .../lib/template_library/types/FloatType.java | 3 +- .../lib/template_library/types/IntType.java | 3 +- .../lib/template_library/types/LongType.java | 3 +- .../template_library/types/PrimitiveType.java | 36 +++++++++++++++++++ .../examples/TestFuzzExpression.java | 2 ++ 9 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/PrimitiveType.java diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Type.java b/test/hotspot/jtreg/compiler/lib/template_library/Type.java index 11dae41469322..063bd04c54cda 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Type.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Type.java @@ -25,6 +25,8 @@ import java.util.List; +import compiler.lib.template_framework.Name; + import compiler.lib.template_library.types.IntType; import compiler.lib.template_library.types.LongType; import compiler.lib.template_library.types.FloatType; @@ -37,7 +39,7 @@ * * TODO */ -public abstract class Type { +public abstract class Type implements Name.Type { public static final List primitives() { return List.of( diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Value.java b/test/hotspot/jtreg/compiler/lib/template_library/Value.java index d740d4beeed98..c55e1963289ca 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Value.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Value.java @@ -26,15 +26,23 @@ import java.util.ArrayList; import java.util.List; import java.util.HashSet; +import java.util.Random; +import jdk.test.lib.Utils; import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.Name; import compiler.lib.template_framework.TemplateWithArgs; import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.addName; +import static compiler.lib.template_framework.Template.weighNames; +import static compiler.lib.template_framework.Template.sampleName; /** * TODO: description + * How are we allowed to use this? To make sure that the Names are available! */ public record Value(Object defTokens, Object useTokens) { + private static final Random RANDOM = Utils.getRandomInstance(); public static Value fromUseToken(Object useToken) { return new Value("", useToken); @@ -42,6 +50,13 @@ public static Value fromUseToken(Object useToken) { public static Value makeRandom(Type type) { // TODO: more cases + // Read existing Names + // Method argument Names? + // Create new Name: field or variable + if (RANDOM.nextInt(3) == 0 && weighNames(type, false) > 0) { + Name name = sampleName(type, false); + return fromUseToken(name.name()); + } return fromUseToken(type.con()); } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java index 8093590c25213..919a80e9a007a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java @@ -28,10 +28,9 @@ import java.util.Random; import jdk.test.lib.Utils; -import compiler.lib.template_library.Type; import compiler.lib.template_library.Operation; -public final class BooleanType extends Type { +public final class BooleanType extends PrimitiveType { public static final BooleanType INSTANCE = new BooleanType(); private static final Random RANDOM = Utils.getRandomInstance(); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java index fe37f4dc35d2f..7bfa6cbc059b9 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java @@ -28,10 +28,9 @@ import compiler.lib.generators.Generators; import compiler.lib.generators.Generator; -import compiler.lib.template_library.Type; import compiler.lib.template_library.Operation; -public final class DoubleType extends Type { +public final class DoubleType extends PrimitiveType { public static final DoubleType INSTANCE = new DoubleType(); private static final Generator GEN_DOUBLE = Generators.G.doubles(); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java index a4b94787ec775..5882588f93998 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java @@ -28,10 +28,9 @@ import compiler.lib.generators.Generators; import compiler.lib.generators.Generator; -import compiler.lib.template_library.Type; import compiler.lib.template_library.Operation; -public final class FloatType extends Type { +public final class FloatType extends PrimitiveType { public static final FloatType INSTANCE = new FloatType(); private static final Generator GEN_FLOAT = Generators.G.floats(); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java index cbbe7d3e8896e..29120f18cd9bf 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java @@ -28,10 +28,9 @@ import compiler.lib.generators.Generators; import compiler.lib.generators.RestrictableGenerator; -import compiler.lib.template_library.Type; import compiler.lib.template_library.Operation; -public final class IntType extends Type { +public final class IntType extends PrimitiveType { public static final IntType INSTANCE = new IntType(); private static final RestrictableGenerator GEN_INT = Generators.G.ints(); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java index d6b260ffd56bf..58980e6b935a5 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java @@ -28,10 +28,9 @@ import compiler.lib.generators.Generators; import compiler.lib.generators.RestrictableGenerator; -import compiler.lib.template_library.Type; import compiler.lib.template_library.Operation; -public final class LongType extends Type { +public final class LongType extends PrimitiveType { public static final LongType INSTANCE = new LongType(); private static final RestrictableGenerator GEN_LONG = Generators.G.longs(); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/PrimitiveType.java new file mode 100644 index 0000000000000..0ffc43e5013f9 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/PrimitiveType.java @@ -0,0 +1,36 @@ +/* + * 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_library.types; + +import compiler.lib.template_framework.Name; +import compiler.lib.template_library.Type; + +public abstract class PrimitiveType extends Type { + + @Override + public boolean isSubtypeOf(Name.Type other) { + // Primitives are only subtypes of their own Primitive type. + return this.getClass() == other.getClass(); + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index df7afb6861a5c..91452beae4bec 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -164,6 +164,8 @@ public static String generate(CompileFramework comp) { ); }); + // TODO: hand-unrollling case + // Use template1 100 times with every type. List templates = new ArrayList<>(); for (int i = 0; i < 50; i++) { From c8b10320af6dcddc079ebe8a9705512df7814afd Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 24 Feb 2025 10:47:34 +0100 Subject: [PATCH 114/212] Hook.isSet --- .../lib/template_framework/CodeFrame.java | 11 ++++++ .../compiler/lib/template_framework/Hook.java | 9 +++++ .../lib/template_framework/Renderer.java | 15 ++++---- .../compiler/lib/template_library/Value.java | 7 ++-- .../tests/TestTemplate.java | 35 +++++++++++++++++++ 5 files changed, 68 insertions(+), 9 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java index 0aecbe4ebbb18..c1936da4ad93b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java @@ -119,6 +119,17 @@ boolean hasHook(Hook hook) { return hookCodeLists.containsKey(hook); } + CodeFrame codeFrameForHook(Hook hook) { + CodeFrame current = this; + while (current != null) { + if (current.hasHook(hook)) { + return current; + } + current = current.parent; + } + return null; + } + void addName(Name name) { names.add(name); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java index 2863bcf277a8f..54f922f62d734 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java @@ -89,4 +89,13 @@ public Token set(Object... tokens) { public Token insert(TemplateWithArgs templateWithArgs) { return new HookInsertToken(this, templateWithArgs); } + + /** + * Checks if the {@link Hook} was {@link Hook#set} for the current scope or an outer scope. + * + * @return If the {@link Hook} was {@link Hook#set} for the current scope or an outer scope. + */ + public boolean isSet() { + return Renderer.getCurrent().isSet(this); + } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index d8411a58fdd5a..11e38ad72a70b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -286,14 +286,15 @@ private String templateString(String s) { ); } + boolean isSet(Hook hook) { + return currentCodeFrame.codeFrameForHook(hook) != null; + } + private CodeFrame codeFrameForHook(Hook hook) { - CodeFrame codeFrame = currentCodeFrame; - while (codeFrame != null) { - if (codeFrame.hasHook(hook)) { - return codeFrame; - } - codeFrame = codeFrame.parent; + CodeFrame codeFrame = currentCodeFrame.codeFrameForHook(hook); + if (codeFrame == null) { + throw new RendererException("Hook '" + hook.name() + "' was referenced but not found!"); } - throw new RendererException("Hook '" + hook.name() + "' was referenced but not found!"); + return codeFrame; } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Value.java b/test/hotspot/jtreg/compiler/lib/template_library/Value.java index c55e1963289ca..1124e107b3751 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Value.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Value.java @@ -50,13 +50,16 @@ public static Value fromUseToken(Object useToken) { public static Value makeRandom(Type type) { // TODO: more cases - // Read existing Names // Method argument Names? - // Create new Name: field or variable + + // Load from existing Name (e.g. variable or field). if (RANDOM.nextInt(3) == 0 && weighNames(type, false) > 0) { Name name = sampleName(type, false); return fromUseToken(name.name()); } + // Define new field, load from it. + if (RANDOM.nextInt(4) == 0 && Library.CLASS_HOOK.isSet()) { + } return fromUseToken(type.con()); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index c7cd5181e4c52..2ae4eac3025c1 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -76,6 +76,7 @@ public static void main(String[] args) { testWithTwoArguments(); testNested(); testHookSimple(); + testHookIsSet(); testHookNested(); testHookWithNestedTemplates(); testHookRecursion(); @@ -97,6 +98,7 @@ public static void main(String[] args) { expectRendererException(() -> setFuelCost(1.0f), "A Template method such as"); expectRendererException(() -> weighNames(myInt, true), "A Template method such as"); expectRendererException(() -> sampleName(myInt, true), "A Template method such as"); + expectRendererException(() -> (new Hook("abc")).isSet(), "A Template method such as"); expectRendererException(() -> testFailingHook(), "Hook 'Hook1' was referenced but not found!"); expectRendererException(() -> testFailingSample(), "No variable of type 'int'."); expectRendererException(() -> testFailingHashtag1(), "Duplicate hashtag replacement for #a"); @@ -270,6 +272,39 @@ public static void testHookSimple() { checkEQ(code, expected); } + public static void testHookIsSet() { + var hook1 = new Hook("Hook1"); + + var template0 = Template.make(() -> body("isSet: ", hook1.isSet(), "\n")); + + var template1 = Template.make(() -> body("Hello\n", template0.withArgs())); + + var template2 = Template.make(() -> body( + "{\n", + template0.withArgs(), + hook1.set( + "World\n", + template0.withArgs(), + hook1.insert(template1.withArgs()) + ), + template0.withArgs(), + "}" + )); + + String code = template2.withArgs().render(); + String expected = + """ + { + isSet: false + Hello + isSet: true + World + isSet: true + isSet: false + }"""; + checkEQ(code, expected); + } + public static void testHookNested() { var hook1 = new Hook("Hook1"); From 8268427ddee8080aaaadf9a5100c75aee04ec9a0 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 24 Feb 2025 12:51:51 +0100 Subject: [PATCH 115/212] defineField --- .../lib/template_library/Library.java | 20 +++++++++++++++++++ .../compiler/lib/template_library/Value.java | 7 +++++++ 2 files changed, 27 insertions(+) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Library.java b/test/hotspot/jtreg/compiler/lib/template_library/Library.java index b778efba3a727..4732c848cb3ff 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Library.java @@ -28,6 +28,12 @@ import jdk.test.lib.Utils; import compiler.lib.template_framework.Hook; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.Name; +import compiler.lib.template_framework.TemplateWithArgs; +import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.let; +import static compiler.lib.template_framework.Template.addName; /** * TODO @@ -48,4 +54,18 @@ static T choice(List list) { int i = RANDOM.nextInt(list.size()); return list.get(i); } + + public static TemplateWithArgs defineField(Name name, boolean isStatic, Object valueToken) { + var define = Template.make(() -> body( + let("static", isStatic ? "static" : ""), + let("type", name.type()), + let("name", name.name()), + "public #static #type #name = ", valueToken, ";\n", + addName(name) + )); + var template = Template.make(() -> body( + CLASS_HOOK.insert(define.withArgs()) + )); + return template.withArgs(); + } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Value.java b/test/hotspot/jtreg/compiler/lib/template_library/Value.java index 1124e107b3751..34a29786987ab 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Value.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Value.java @@ -33,6 +33,7 @@ import compiler.lib.template_framework.Name; import compiler.lib.template_framework.TemplateWithArgs; import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.$; import static compiler.lib.template_framework.Template.addName; import static compiler.lib.template_framework.Template.weighNames; import static compiler.lib.template_framework.Template.sampleName; @@ -43,6 +44,7 @@ */ public record Value(Object defTokens, Object useTokens) { private static final Random RANDOM = Utils.getRandomInstance(); + private static int nextUniqueId = 0; public static Value fromUseToken(Object useToken) { return new Value("", useToken); @@ -59,6 +61,11 @@ public static Value makeRandom(Type type) { } // Define new field, load from it. if (RANDOM.nextInt(4) == 0 && Library.CLASS_HOOK.isSet()) { + String fieldName = $("field") + "_" + (nextUniqueId++); + Name name = new Name(fieldName, type, false, 1); + Object value = type.con(); + TemplateWithArgs def = Library.defineField(name, true, value); + return new Value(def, fieldName); } return fromUseToken(type.con()); } From 5247647c10bf30b622c51ec36c7a51870ddfb577 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 24 Feb 2025 13:00:27 +0100 Subject: [PATCH 116/212] set class hook in test --- .../examples/TestFuzzExpression.java | 94 ++++++++++--------- 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index 91452beae4bec..a242a4b06e8a8 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -94,42 +94,46 @@ public static String generate(CompileFramework comp) { List def = argValues.stream().map(v -> v.defTokens()).toList(); List use = argValues.stream().map(v -> v.useTokens()).toList(); return body( - def, """ // --- $test start --- // Using $reference // type: #type - - @DontCompile - public static Object $reference() { - try { """, - " return ", expression.withArgs(use), ";\n", - """ - } catch (Exception e) { - return e; + Library.CLASS_HOOK.set( + def, + """ + + @DontCompile + public static Object $reference() { + try { + """, + " return ", expression.withArgs(use), ";\n", + """ + } catch (Exception e) { + return e; + } } - } - @Test - public static Object $test() { - try { - """, - " return ", expression.withArgs(use), ";\n", - """ - } catch (Exception e) { - return e; + @Test + public static Object $test() { + try { + """, + " return ", expression.withArgs(use), ";\n", + """ + } catch (Exception e) { + return e; + } } - } - @Check(test = "$test") - public static void $check(Object result) { - Object gold = $reference(); - Verify.checkEQ(result, gold); - } + @Check(test = "$test") + public static void $check(Object result) { + Object gold = $reference(); + Verify.checkEQ(result, gold); + } - // --- $test end --- - """ + // --- $test end --- + """ + ) ); }); @@ -140,27 +144,31 @@ public static String generate(CompileFramework comp) { // --- $test start --- // Using $GOLD // type: #type - - static final Object $GOLD = $test(); - - @Test - public static Object $test() { - try { """, - " return ", expression.withRandomArgs(), ";\n", - """ - } catch (Exception e) { - return e; + Library.CLASS_HOOK.set( + """ + + static final Object $GOLD = $test(); + + @Test + public static Object $test() { + try { + """, + " return ", expression.withRandomArgs(), ";\n", + """ + } catch (Exception e) { + return e; + } } - } - @Check(test = "$test") - public static void $check(Object result) { - Verify.checkEQ(result, $GOLD); - } + @Check(test = "$test") + public static void $check(Object result) { + Verify.checkEQ(result, $GOLD); + } - // --- $test end --- - """ + // --- $test end --- + """ + ) ); }); From 87ba30d61b3c0a069cb02684d1437e6f8458ac39 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 24 Feb 2025 14:40:21 +0100 Subject: [PATCH 117/212] 3 args, and array test --- .../lib/template_framework/Template.java | 72 +++++++++++++++++++ .../template_framework/TemplateWithArgs.java | 37 +++++++++- .../lib/template_framework/Token.java | 1 + .../jtreg/compiler/lib/verify/Verify.java | 18 +++++ .../tests/TestTemplate.java | 27 +++++++ .../examples/TestFuzzExpression.java | 63 +++++++++++++++- 6 files changed, 216 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index da35fb8d88e6e..047621c2c3332 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -153,6 +153,45 @@ static TwoArgs make(String arg0Name, String arg1Name, BiFunction(arg0Name, arg1Name, body); } + /** + * Interface for function with three arguments. + * + * @param Type of the first argument. + * @param Type of the second argument. + * @param Type of the third argument. + * @param Type of the return value. + */ + @FunctionalInterface + public interface TriFunction { + + /** + * Function definition for the three argument functions. + * + * @param t The first argument. + * @param u The second argument. + * @param v The third argument. + * @return Return value of the three argument function. + */ + R apply(T t, U u, V v); + } + + /** + * Creates a {@link Template} with three arguments. + * See {@link body} for more details about how to construct a {@link Template} with {@link Token}s. + * + * @param body The {@link TemplateBody} created by {@link Template#body}. + * @param Type of the first argument. + * @param arg0Name The name of the first argument for hashtag replacement. + * @param Type of the second argument. + * @param arg1Name The name of the second argument for hashtag replacement. + * @param Type of the third argument. + * @param arg2Name The name of the third argument for hashtag replacement. + * @return A {@link Template} with three arguments. + */ + static ThreeArgs make(String arg0Name, String arg1Name, String arg2Name, TriFunction body) { + return new ThreeArgs<>(arg0Name, arg1Name, arg2Name, body); + } + /** * A {@link Template} with no arguments. * @@ -233,6 +272,39 @@ public TemplateWithArgs withArgs(A a, B b) { } } + /** + * A {@link Template} with three arguments. + * + * @param arg0Name The name of the first argument, used for hashtag replacements in the {@link Template}. + * @param arg1Name The name of the second argument, used for hashtag replacements in the {@link Template}. + * @param arg2Name The name of the third argument, used for hashtag replacements in the {@link Template}. + * @param The type of the first argument. + * @param The type of the second argument. + * @param The type of the third argument. + * @param function The function with three arguments that creates the {@link TemplateBody} given the template arguments. + */ + record ThreeArgs(String arg0Name, String arg1Name, String arg2Name, + TriFunction function) implements Template { + TemplateBody instantiate(A a, B b, C c) { + return function.apply(a, b, c); + } + + /** + * Creates a {@link TemplateWithArgs} which can be used as a {@link Token} inside + * a {@link Template} for nested code generation, and it can also be used with + * {@link TemplateWithArgs#render} to render the template to a {@link String} + * directly. + * + * @param a The value for the first argument. + * @param b The value for the second argument. + * @param c The value for the third argument. + * @return The template all (three) arguments applied. + */ + public TemplateWithArgs withArgs(A a, B b, C c) { + return new TemplateWithArgs.ThreeArgsUse<>(this, a, b, c); + } + } + /** * Creates a {@link TemplateBody} from a list of tokens, which can be {@link String}s, * boxed primitive types (e.g. {@link Integer}), any {@link Token}, or {@link List}s diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java index 308da9dc63ada..8db8641e2634e 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateWithArgs.java @@ -30,7 +30,8 @@ public sealed abstract class TemplateWithArgs implements Token permits TemplateWithArgs.ZeroArgsUse, TemplateWithArgs.OneArgsUse, - TemplateWithArgs.TwoArgsUse + TemplateWithArgs.TwoArgsUse, + TemplateWithArgs.ThreeArgsUse { private TemplateWithArgs() {} @@ -110,6 +111,40 @@ public void visitArguments(ArgumentVisitor visitor) { } } + /** + * Represents a three-argument {@link Template} with applied arguments, ready for instantiation + * either as a {@link Token} inside another {@link Template} or with {@link render}. + * + * @param The type of the first argument. + * @param The type of the second argument. + * @param The type of the second argument. + */ + public static final class ThreeArgsUse extends TemplateWithArgs implements Token { + private final Template.ThreeArgs threeArgs; + private final A a; + private final B b; + private final C c; + + ThreeArgsUse(Template.ThreeArgs threeArgs, A a, B b, C c) { + this.threeArgs = threeArgs; + this.a = a; + this.b = b; + this.c = c; + } + + @Override + public TemplateBody instantiate() { + return threeArgs.instantiate(a, b, c); + } + + @Override + public void visitArguments(ArgumentVisitor visitor) { + visitor.visit(threeArgs.arg0Name(), a); + visitor.visit(threeArgs.arg1Name(), b); + visitor.visit(threeArgs.arg2Name(), c); + } + } + abstract TemplateBody instantiate(); @FunctionalInterface diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java index 4ea1b0eeea8b3..bcf3d7578ebd4 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java @@ -38,6 +38,7 @@ sealed interface Token permits StringToken, TemplateWithArgs.ZeroArgsUse, TemplateWithArgs.OneArgsUse, TemplateWithArgs.TwoArgsUse, + TemplateWithArgs.ThreeArgsUse, HookSetToken, HookInsertToken, AddNameToken, diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index a40d6a929c59f..7279b5848620e 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -94,6 +94,7 @@ private static void checkEQ(Object a, Object b, String context) { case long[] x -> checkEQimpl(x, (long[])b, context); case float[] x -> checkEQimpl(x, (float[])b, context); case double[] x -> checkEQimpl(x, (double[])b, context); + case boolean[] x -> checkEQimpl(x, (boolean[])b, context); case MemorySegment x -> checkEQimpl(x, (MemorySegment) b, context); case Exception x -> checkEQimpl(x, (Exception) b, context); default -> { @@ -288,6 +289,23 @@ private static void checkEQimpl(double[] a, double[] b, String context) { checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), context); } + /** + * Verify that the content of two boolean arrays is identical. + */ + private static void checkEQimpl(boolean[] a, boolean[] b, String context) { + if (a.length != b.length) { + System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length + " vs " + b.length + " " + context); + throw new VerifyException("Object array length mismatch."); + } + + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i] + " " + context); + throw new VerifyException("Boolean array value mismatch."); + } + } + } + /** * Verify that the content of two Object arrays is identical, recursively: * every element is compared with checkEQimpl for the corresponding type. diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 2ae4eac3025c1..0bdd76c204e16 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -74,6 +74,7 @@ public static void main(String[] args) { testBodyTokens(); testWithOneArguments(); testWithTwoArguments(); + testWithThreeArguments(); testNested(); testHookSimple(); testHookIsSet(); @@ -206,6 +207,32 @@ public static void testWithTwoArguments() { checkEQ(template4.withArgs(444, 555).render(), "start 444 555 end"); } + public static void testWithThreeArguments() { + // Capture 3 String arguments via String names. + var template1 = Template.make("a1", "a2", "a3", (String a1, String a2, String a3) -> body("start #a1 #a2 #a3 end")); + checkEQ(template1.withArgs("x", "y", "z").render(), "start x y z end"); + checkEQ(template1.withArgs("a", "b", "c").render(), "start a b c end"); + checkEQ(template1.withArgs("", "", "" ).render(), "start end"); + + // Capture 3 String arguments via typed lambda arguments. + var template2 = Template.make("a1", "a2", "a3", (String a1, String a2, String a3) -> body("start ", a1, " ", a2, " ", a3, " end")); + checkEQ(template1.withArgs("x", "y", "z").render(), "start x y z end"); + checkEQ(template1.withArgs("a", "b", "c").render(), "start a b c end"); + checkEQ(template1.withArgs("", "", "" ).render(), "start end"); + + // Capture 3 Integer arguments via String names. + var template3 = Template.make("a1", "a2", "a3", (Integer a1, Integer a2, Integer a3) -> body("start #a1 #a2 #a3 end")); + checkEQ(template3.withArgs(0, 1 , 2 ).render(), "start 0 1 2 end"); + checkEQ(template3.withArgs(22, 33 , 44 ).render(), "start 22 33 44 end"); + checkEQ(template3.withArgs(444, 555, 666).render(), "start 444 555 666 end"); + + // Capture 2 Integer arguments via templated lambda arguments. + var template4 = Template.make("a1", "a2", "a3", (Integer a1, Integer a2, Integer a3) -> body("start ", a1, " ", a2, " ", a3, " end")); + checkEQ(template3.withArgs(0, 1 , 2 ).render(), "start 0 1 2 end"); + checkEQ(template3.withArgs(22, 33 , 44 ).render(), "start 22 33 44 end"); + checkEQ(template3.withArgs(444, 555, 666).render(), "start 444 555 666 end"); + } + public static void testNested() { var template1 = Template.make(() -> body("proton")); diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index a242a4b06e8a8..5b6d9ad713a54 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -45,6 +45,7 @@ import compiler.lib.template_framework.TemplateWithArgs; 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_library.Library; import compiler.lib.template_library.IRTestClass; @@ -172,14 +173,74 @@ public static String generate(CompileFramework comp) { ); }); + var defineArray = Template.make("type", "name", "size", (Type type, String name, Integer size) -> body( + """ + public static #type[] #name = new #type[#size]; + """ + )); + + var template3 = Template.make("type", (Type type)-> { + int size = 10_000; // TODO: randomize + Expression expression = Expression.make(type, Type.primitives(), 2); + List types = expression.types(); + List arrayDefinitions = new ArrayList<>(); + List args = new ArrayList<>(); + for (int i = 0; i < types.size(); i++) { + String name = $("array") + "_" + i; + arrayDefinitions.add(defineArray.withArgs(types.get(i), name, size)); + args.add(name + "[i]"); + } + return body( + let("size", size), + """ + // --- $test start --- + // Using $GOLD + // type: #type + """, + Library.CLASS_HOOK.set( + """ + // Input arrays: + """, + arrayDefinitions, + """ + + static final Object $GOLD = $test(); + + @Test + public static Object $test() { + try { + #type[] out = new #type[#size]; + for (int i = 0; i < out.length; i++) { + """, + " out[i] = ", expression.withArgs(args), ";\n", + """ + } + return out; + } catch (Exception e) { + return e; + } + } + + @Check(test = "$test") + public static void $check(Object result) { + Verify.checkEQ(result, $GOLD); + } + + // --- $test end --- + """ + ) + ); + }); + // TODO: hand-unrollling case // Use template1 100 times with every type. List templates = new ArrayList<>(); - for (int i = 0; i < 50; i++) { + for (int i = 0; i < 5; i++) { for (Type type : Type.primitives()) { templates.add(template1.withArgs(type)); templates.add(template2.withArgs(type)); + templates.add(template3.withArgs(type)); } } return IRTestClass.TEMPLATE.withArgs(info, templates).render(); From 80c70b50806bdaf1edaebfca4d32b097005741ca Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 24 Feb 2025 16:09:37 +0100 Subject: [PATCH 118/212] more test descriptions --- .../template_library/examples/TestFuzzExpression.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index 5b6d9ad713a54..4b9dbcbef3d05 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -89,6 +89,11 @@ public static String generate(CompileFramework comp) { List.of("compiler.lib.generators.*", "compiler.lib.verify.*")); + // Example 1: + // We use the "expression" twice: once in a reference method that runs in the interpreter, + // once in the test method that is compiled. Then we compare the results. + // The argValues may want to define fields in "def" so that they can load from them in "use". + // We only want to do the "def" part once, but the "use" part twice, so we have to split it. var template1 = Template.make("type", (Type type)-> { Expression expression = Expression.make(type, Type.primitives(), 2); List argValues = expression.randomArgValues(); @@ -138,6 +143,9 @@ public static String generate(CompileFramework comp) { ); }); + // Example 2: + // We only use the "expression" once, and so we can conveniently just run it with + // random arguments. Those may also generate their own fields under the hood. var template2 = Template.make("type", (Type type)-> { Expression expression = Expression.make(type, Type.primitives(), 2); return body( @@ -179,6 +187,9 @@ public static String generate(CompileFramework comp) { """ )); + // Example 3: + // We use the expression to iterate over arrays, loading from a set of input arrays, + // and storing to an output array. var template3 = Template.make("type", (Type type)-> { int size = 10_000; // TODO: randomize Expression expression = Expression.make(type, Type.primitives(), 2); From a95147ba189e02289c19af3b7725d732b6eda196 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 24 Feb 2025 17:07:26 +0100 Subject: [PATCH 119/212] using local variables for expression input values --- .../lib/template_library/Library.java | 15 ++++++++++ .../compiler/lib/template_library/Value.java | 5 ++++ .../examples/TestFuzzExpression.java | 30 +++++++++++++++---- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Library.java b/test/hotspot/jtreg/compiler/lib/template_library/Library.java index 4732c848cb3ff..27fe88030fda8 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Library.java @@ -68,4 +68,19 @@ public static TemplateWithArgs defineField(Name name, boolean isStatic, Object v )); return template.withArgs(); } + + public static TemplateWithArgs defineVariableWithComputation(String name, Type type) { + Name n = new Name(name, type, false, 1); + // TODO: more patterns + var define = Template.make(() -> body( + let("type", type), + let("name", name), + "#type #name = ", type.con(), ";\n", + addName(n) + )); + var template = Template.make(() -> body( + METHOD_HOOK.insert(define.withArgs()) + )); + return template.withArgs(); + } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Value.java b/test/hotspot/jtreg/compiler/lib/template_library/Value.java index 34a29786987ab..5517d4cd43cc6 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Value.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Value.java @@ -67,6 +67,11 @@ public static Value makeRandom(Type type) { TemplateWithArgs def = Library.defineField(name, true, value); return new Value(def, fieldName); } + if (RANDOM.nextInt(4) == 0 && Library.METHOD_HOOK.isSet()) { + String varName = $("var") + "_" + (nextUniqueId++); + TemplateWithArgs def = Library.defineVariableWithComputation(varName, type); + return new Value(def, varName); + } return fromUseToken(type.con()); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index 4b9dbcbef3d05..385e4a9ff3607 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -94,6 +94,9 @@ public static String generate(CompileFramework comp) { // once in the test method that is compiled. Then we compare the results. // The argValues may want to define fields in "def" so that they can load from them in "use". // We only want to do the "def" part once, but the "use" part twice, so we have to split it. + // + // Note: we are using the "expression" in two separate methods, so we cannot generate any + // fields with "def", as we would have to generate them identically twice. var template1 = Template.make("type", (Type type)-> { Expression expression = Expression.make(type, Type.primitives(), 2); List argValues = expression.randomArgValues(); @@ -146,8 +149,25 @@ public static String generate(CompileFramework comp) { // Example 2: // We only use the "expression" once, and so we can conveniently just run it with // random arguments. Those may also generate their own fields under the hood. - var template2 = Template.make("type", (Type type)-> { + // + // Note: we put the expression in a separate Template so that the method and class + // hook are both already set before we call "expression.withRandomArgs", and so that + // we know we can generate fields and local variables. + var template2Body = Template.make("type", (Type type)-> { Expression expression = Expression.make(type, Type.primitives(), 2); + return body( + """ + try { + """, + " return ", expression.withRandomArgs(), ";\n", + """ + } catch (Exception e) { + return e; + } + """ + ); + }); + var template2 = Template.make("type", (Type type)-> { return body( """ // --- $test start --- @@ -161,13 +181,11 @@ public static String generate(CompileFramework comp) { @Test public static Object $test() { - try { """, - " return ", expression.withRandomArgs(), ";\n", + Library.METHOD_HOOK.set( + template2Body.withArgs(type) + ), """ - } catch (Exception e) { - return e; - } } @Check(test = "$test") From b93c4f65010d43d8bae83c8d80c504a37b8afde3 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 25 Feb 2025 13:37:07 +0100 Subject: [PATCH 120/212] larger expressions, random size, and fix float array verification for NaN --- .../lib/template_library/Expression.java | 6 +++- .../jtreg/compiler/lib/verify/Verify.java | 32 +++++++++++++++++-- .../examples/TestFuzzExpression.java | 6 ++-- 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index 37dfda641ce84..a0c193a73e134 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -26,6 +26,8 @@ import java.util.ArrayList; import java.util.List; import java.util.HashSet; +import java.util.Random; +import jdk.test.lib.Utils; import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateWithArgs; @@ -37,6 +39,8 @@ * Idea: generates a template that has a list of {@link Type} holes. */ public final class Expression { + private static final Random RANDOM = Utils.getRandomInstance(); + private final Template.OneArgs> template; private final List types; @@ -104,7 +108,7 @@ private static final ExpressionGenerator expressionGenerator(Type resultType, Ha private static final ExpressionGeneratorStep expressionGeneratorStep(Type resultType, HashSet allowedTypes, int maxDepth, List types) { List ops = resultType.operations().stream().filter(o -> o.hasOnlyTypes(allowedTypes)).toList(); - if (maxDepth <= 0 || ops.isEmpty()) { + if (maxDepth <= 0 || ops.isEmpty() || RANDOM.nextInt(2 * maxDepth) == 0) { // Remember which type we need to fill the ith argument with. int i = types.size(); types.add(resultType); diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index 7279b5848620e..3c0878203955d 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -277,16 +277,42 @@ private static void checkEQimpl(long[] a, long[] b, String context) { /** * Verify that the content of two float arrays is identical. + * Ideally, we would want to assert that the Float.floatToRawIntBits are identical. + * But the Java spec allows us to return different bits for a NaN, which allows swapping the inputs + * of an add or mul (NaN1 * NaN2 does not have same bits as NaN2 * NaN1, because the multiplication + * of two NaN values should always return the first of the two). So we verify that we have the same bit + * pattern in all cases, except for NaN we project to the canonical NaN, using Float.floatToIntBits. */ private static void checkEQimpl(float[] a, float[] b, String context) { - checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), context); + if (a.length != b.length) { + System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length + " vs " + b.length + " " + context); + throw new VerifyException("Float array length mismatch."); + } + + for (int i = 0; i < a.length; i++) { + if (Float.floatToIntBits(a[i]) != Float.floatToIntBits(b[i])) { + System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i] + " " + context); + throw new VerifyException("Float array value mismatch."); + } + } } /** * Verify that the content of two double arrays is identical. + * Same issue with NaN as above for floats. */ private static void checkEQimpl(double[] a, double[] b, String context) { - checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), context); + if (a.length != b.length) { + System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length + " vs " + b.length + " " + context); + throw new VerifyException("Double array length mismatch."); + } + + for (int i = 0; i < a.length; i++) { + if (Double.doubleToLongBits(a[i]) != Double.doubleToLongBits(b[i])) { + System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i] + " " + context); + throw new VerifyException("Double array value mismatch."); + } + } } /** @@ -295,7 +321,7 @@ private static void checkEQimpl(double[] a, double[] b, String context) { private static void checkEQimpl(boolean[] a, boolean[] b, String context) { if (a.length != b.length) { System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length + " vs " + b.length + " " + context); - throw new VerifyException("Object array length mismatch."); + throw new VerifyException("Boolean array length mismatch."); } for (int i = 0; i < a.length; i++) { diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index 385e4a9ff3607..40ab43fb34413 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -98,7 +98,7 @@ public static String generate(CompileFramework comp) { // Note: we are using the "expression" in two separate methods, so we cannot generate any // fields with "def", as we would have to generate them identically twice. var template1 = Template.make("type", (Type type)-> { - Expression expression = Expression.make(type, Type.primitives(), 2); + Expression expression = Expression.make(type, Type.primitives(), 4); List argValues = expression.randomArgValues(); List def = argValues.stream().map(v -> v.defTokens()).toList(); List use = argValues.stream().map(v -> v.useTokens()).toList(); @@ -154,7 +154,7 @@ public static String generate(CompileFramework comp) { // hook are both already set before we call "expression.withRandomArgs", and so that // we know we can generate fields and local variables. var template2Body = Template.make("type", (Type type)-> { - Expression expression = Expression.make(type, Type.primitives(), 2); + Expression expression = Expression.make(type, Type.primitives(), 4); return body( """ try { @@ -210,7 +210,7 @@ public static String generate(CompileFramework comp) { // and storing to an output array. var template3 = Template.make("type", (Type type)-> { int size = 10_000; // TODO: randomize - Expression expression = Expression.make(type, Type.primitives(), 2); + Expression expression = Expression.make(type, Type.primitives(), 4); List types = expression.types(); List arrayDefinitions = new ArrayList<>(); List args = new ArrayList<>(); From 742265e7bf1023a9300a4e2cfe7b6510ef9a5668 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 25 Feb 2025 14:40:26 +0100 Subject: [PATCH 121/212] fill arrays in test --- .../lib/template_library/Library.java | 40 +++++++++++++++++++ .../examples/TestFuzzExpression.java | 4 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Library.java b/test/hotspot/jtreg/compiler/lib/template_library/Library.java index 27fe88030fda8..73c979d66b70a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Library.java @@ -83,4 +83,44 @@ public static TemplateWithArgs defineVariableWithComputation(String name, Type t )); return template.withArgs(); } + + public static TemplateWithArgs arrayFillMethods() { + var template = Template.make(() -> body( + """ + private static final RestrictableGenerator GEN_INT = Generators.G.ints(); + private static final RestrictableGenerator GEN_LONG = Generators.G.longs(); + private static final Generator GEN_FLOAT = Generators.G.floats(); + private static final Generator GEN_DOUBLE = Generators.G.doubles(); + private static final RestrictableGenerator GEN_BOOLEAN = Generators.G.safeRestrict(Generators.G.ints(), 0, 1); + + public static int[] fill(int[] a) { + Generators.G.fill(GEN_INT, a); + return a; + } + + public static long[] fill(long[] a) { + Generators.G.fill(GEN_LONG, a); + return a; + } + + public static float[] fill(float[] a) { + Generators.G.fill(GEN_FLOAT, a); + return a; + } + + public static double[] fill(double[] a) { + Generators.G.fill(GEN_DOUBLE, a); + return a; + } + + public static boolean[] fill(boolean[] a) { + for (int i = 0; i < a.length; i++) { + a[i] = GEN_BOOLEAN.next() == 1; + } + return a; + } + """ + )); + return template.withArgs(); + } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index 40ab43fb34413..e0bae0b6cb2c1 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -201,7 +201,7 @@ public static String generate(CompileFramework comp) { var defineArray = Template.make("type", "name", "size", (Type type, String name, Integer size) -> body( """ - public static #type[] #name = new #type[#size]; + public static #type[] #name = fill(new #type[#size]); """ )); @@ -263,8 +263,10 @@ public static String generate(CompileFramework comp) { // TODO: hand-unrollling case + // Use template1 100 times with every type. List templates = new ArrayList<>(); + templates.add(Library.arrayFillMethods()); for (int i = 0; i < 5; i++) { for (Type type : Type.primitives()) { templates.add(template1.withArgs(type)); From 3289d45efc9d3e3404705c349f5528c467a2d68b Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 26 Feb 2025 15:25:27 +0100 Subject: [PATCH 122/212] ByteType --- .../lib/template_library/Library.java | 8 +++ .../compiler/lib/template_library/Type.java | 2 + .../lib/template_library/types/ByteType.java | 67 +++++++++++++++++++ .../lib/template_library/types/IntType.java | 11 +++ .../lib/template_library/types/LongType.java | 3 + 5 files changed, 91 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/ByteType.java diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Library.java b/test/hotspot/jtreg/compiler/lib/template_library/Library.java index 73c979d66b70a..e659519f7535d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Library.java @@ -87,12 +87,20 @@ public static TemplateWithArgs defineVariableWithComputation(String name, Type t public static TemplateWithArgs arrayFillMethods() { var template = Template.make(() -> body( """ + private static final RestrictableGenerator GEN_BYTE = Generators.G.safeRestrict(Generators.G.ints(), Byte.MIN_VALUE, Byte.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_FLOAT = Generators.G.floats(); private static final Generator GEN_DOUBLE = Generators.G.doubles(); private static final RestrictableGenerator GEN_BOOLEAN = Generators.G.safeRestrict(Generators.G.ints(), 0, 1); + public static byte[] fill(byte[] a) { + for (int i = 0; i < a.length; i++) { + a[i] = GEN_BYTE.next().byteValue(); + } + return a; + } + public static int[] fill(int[] a) { Generators.G.fill(GEN_INT, a); return a; diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Type.java b/test/hotspot/jtreg/compiler/lib/template_library/Type.java index 063bd04c54cda..6ca213d1369d9 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Type.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Type.java @@ -27,6 +27,7 @@ import compiler.lib.template_framework.Name; +import compiler.lib.template_library.types.ByteType; import compiler.lib.template_library.types.IntType; import compiler.lib.template_library.types.LongType; import compiler.lib.template_library.types.FloatType; @@ -43,6 +44,7 @@ public abstract class Type implements Name.Type { public static final List primitives() { return List.of( + ByteType.INSTANCE, IntType.INSTANCE, LongType.INSTANCE, FloatType.INSTANCE, diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/ByteType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/ByteType.java new file mode 100644 index 0000000000000..a8565d67977cf --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/ByteType.java @@ -0,0 +1,67 @@ +/* + * 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_library.types; + +import java.util.List; + +import compiler.lib.generators.Generators; +import compiler.lib.generators.RestrictableGenerator; + +import compiler.lib.template_library.Operation; + +public final class ByteType extends PrimitiveType { + public static final ByteType INSTANCE = new ByteType(); + private static final RestrictableGenerator GEN_BYTE = Generators.G.safeRestrict(Generators.G.ints(), Byte.MIN_VALUE, Byte.MAX_VALUE); + + private static final List OPERATIONS = List.of( + // Note: the standard integer arithmetic operations are only defined for int/long. + // They can be used for smaller types only via automatic promotion to int, + // and then a cast back to byte, e.g: + // byte a = (byte)(b + c) + // + // Instead of adding these operations explicitly, we just add the conversion + // from int to byte, and let the IntType generate all the integer arithmetic + // operations. + new Operation.Unary("((byte)", IntType.INSTANCE, ")"), + new Operation.Unary("((byte)", LongType.INSTANCE, ")"), + new Operation.Unary("((byte)", FloatType.INSTANCE, ")"), + new Operation.Unary("((byte)", DoubleType.INSTANCE, ")"), + // Note: There is no cast from boolean + + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", ByteType.INSTANCE, " : ", ByteType.INSTANCE, ")") + ); + + @Override + public final String name() { return "byte"; } + + @Override + public final Object con() { + return "(byte)" + GEN_BYTE.next(); + } + + @Override + public final List operations() { + return OPERATIONS; + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java index 29120f18cd9bf..f318697325cb1 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java @@ -35,6 +35,12 @@ public final class IntType extends PrimitiveType { private static final RestrictableGenerator GEN_INT = Generators.G.ints(); private static final List OPERATIONS = List.of( + new Operation.Unary("((int)", ByteType.INSTANCE, ")"), + new Operation.Unary("((int)", LongType.INSTANCE, ")"), + new Operation.Unary("((int)", FloatType.INSTANCE, ")"), + new Operation.Unary("((int)", DoubleType.INSTANCE, ")"), + // Note: There is no cast from boolean + new Operation.Unary("(-(", IntType.INSTANCE, "))"), new Operation.Unary("(~", IntType.INSTANCE, ")"), @@ -50,6 +56,11 @@ public final class IntType extends PrimitiveType { new Operation.Binary("(", IntType.INSTANCE, " >> ", IntType.INSTANCE, ")"), new Operation.Binary("(", IntType.INSTANCE, " >>> ", IntType.INSTANCE, ")"), + // From Byte: + new Operation.Binary("Byte.compare(", ByteType.INSTANCE, ", ", ByteType.INSTANCE, ")"), + new Operation.Binary("Byte.compareUnsigned(", ByteType.INSTANCE, ", ", ByteType.INSTANCE, ")"), + new Operation.Unary("Byte.toUnsignedInt(", ByteType.INSTANCE, ")"), + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", IntType.INSTANCE, " : ", IntType.INSTANCE, ")") ); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java index 58980e6b935a5..4a9763e454bce 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java @@ -50,6 +50,9 @@ public final class LongType extends PrimitiveType { new Operation.Binary("(", LongType.INSTANCE, " >> ", LongType.INSTANCE, ")"), new Operation.Binary("(", LongType.INSTANCE, " >>> ", LongType.INSTANCE, ")"), + // From Byte: + new Operation.Unary("Byte.toUnsignedLong(", ByteType.INSTANCE, ")"), + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", LongType.INSTANCE, " : ", LongType.INSTANCE, ")") ); From d841fe48c17d684f9a91ec75f5bca78c1fadcaa0 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 26 Feb 2025 15:56:34 +0100 Subject: [PATCH 123/212] char and short --- .../lib/template_library/Library.java | 16 +++++ .../compiler/lib/template_library/Type.java | 4 ++ .../template_library/types/BooleanType.java | 2 + .../lib/template_library/types/ByteType.java | 2 + .../lib/template_library/types/CharType.java | 71 +++++++++++++++++++ .../template_library/types/DoubleType.java | 8 +++ .../lib/template_library/types/FloatType.java | 7 ++ .../lib/template_library/types/IntType.java | 9 ++- .../lib/template_library/types/LongType.java | 11 ++- .../lib/template_library/types/ShortType.java | 71 +++++++++++++++++++ .../examples/TestFuzzExpression.java | 10 ++- 11 files changed, 203 insertions(+), 8 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/CharType.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Library.java b/test/hotspot/jtreg/compiler/lib/template_library/Library.java index e659519f7535d..ac6d2866bb255 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Library.java @@ -88,6 +88,8 @@ public static TemplateWithArgs arrayFillMethods() { var template = Template.make(() -> body( """ 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_FLOAT = Generators.G.floats(); @@ -101,6 +103,20 @@ public static byte[] fill(byte[] a) { return a; } + public static char[] fill(char[] a) { + for (int i = 0; i < a.length; i++) { + a[i] = (char)GEN_CHAR.next().intValue(); + } + return a; + } + + public static short[] fill(short[] a) { + for (int i = 0; i < a.length; i++) { + a[i] = GEN_SHORT.next().shortValue(); + } + return a; + } + public static int[] fill(int[] a) { Generators.G.fill(GEN_INT, a); return a; diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Type.java b/test/hotspot/jtreg/compiler/lib/template_library/Type.java index 6ca213d1369d9..4784833f94298 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Type.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Type.java @@ -28,6 +28,8 @@ import compiler.lib.template_framework.Name; import compiler.lib.template_library.types.ByteType; +import compiler.lib.template_library.types.CharType; +import compiler.lib.template_library.types.ShortType; import compiler.lib.template_library.types.IntType; import compiler.lib.template_library.types.LongType; import compiler.lib.template_library.types.FloatType; @@ -45,6 +47,8 @@ public abstract class Type implements Name.Type { public static final List primitives() { return List.of( ByteType.INSTANCE, + CharType.INSTANCE, + ShortType.INSTANCE, IntType.INSTANCE, LongType.INSTANCE, FloatType.INSTANCE, diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java index 919a80e9a007a..d9a4b40db57b6 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java @@ -35,6 +35,8 @@ public final class BooleanType extends PrimitiveType { private static final Random RANDOM = Utils.getRandomInstance(); private static final List OPERATIONS = List.of( + // Note: there is no casting / conversion from an to boolean directly. + new Operation.Unary("(!(", BooleanType.INSTANCE, "))"), new Operation.Binary("(", BooleanType.INSTANCE, " || ", BooleanType.INSTANCE, ")"), diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/ByteType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/ByteType.java index a8565d67977cf..22b3e36ba258a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/ByteType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/ByteType.java @@ -43,6 +43,8 @@ public final class ByteType extends PrimitiveType { // Instead of adding these operations explicitly, we just add the conversion // from int to byte, and let the IntType generate all the integer arithmetic // operations. + new Operation.Unary("((byte)", CharType.INSTANCE, ")"), + new Operation.Unary("((byte)", ShortType.INSTANCE, ")"), new Operation.Unary("((byte)", IntType.INSTANCE, ")"), new Operation.Unary("((byte)", LongType.INSTANCE, ")"), new Operation.Unary("((byte)", FloatType.INSTANCE, ")"), diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/CharType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/CharType.java new file mode 100644 index 0000000000000..427ef97b5249f --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/CharType.java @@ -0,0 +1,71 @@ +/* + * 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_library.types; + +import java.util.List; + +import compiler.lib.generators.Generators; +import compiler.lib.generators.RestrictableGenerator; + +import compiler.lib.template_library.Operation; + +public final class CharType extends PrimitiveType { + public static final CharType INSTANCE = new CharType(); + private static final RestrictableGenerator GEN_CHAR = Generators.G.safeRestrict(Generators.G.ints(), Character.MIN_VALUE, Character.MAX_VALUE); + + private static final List OPERATIONS = List.of( + // Note: the standard integer arithmetic operations are only defined for int/long. + // They can be used for smaller types only via automatic promotion to int, + // and then a cast back to char, e.g: + // char a = (char)(b + c) + // + // Instead of adding these operations explicitly, we just add the conversion + // from int to char, and let the IntType generate all the integer arithmetic + // operations. + new Operation.Unary("((char)", ByteType.INSTANCE, ")"), + new Operation.Unary("((char)", ShortType.INSTANCE, ")"), + new Operation.Unary("((char)", IntType.INSTANCE, ")"), + new Operation.Unary("((char)", LongType.INSTANCE, ")"), + new Operation.Unary("((char)", FloatType.INSTANCE, ")"), + new Operation.Unary("((char)", DoubleType.INSTANCE, ")"), + // Note: There is no cast from boolean + + new Operation.Unary("Character.reverseBytes(", CharType.INSTANCE, ")"), + + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", CharType.INSTANCE, " : ", CharType.INSTANCE, ")") + ); + + @Override + public final String name() { return "char"; } + + @Override + public final Object con() { + return "(char)" + GEN_CHAR.next(); + } + + @Override + public final List operations() { + return OPERATIONS; + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java index 7bfa6cbc059b9..894eb9c818362 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java @@ -35,6 +35,14 @@ public final class DoubleType extends PrimitiveType { private static final Generator GEN_DOUBLE = Generators.G.doubles(); private static final List OPERATIONS = List.of( + new Operation.Unary("((double)", ByteType.INSTANCE, ")"), + new Operation.Unary("((double)", CharType.INSTANCE, ")"), + new Operation.Unary("((double)", ShortType.INSTANCE, ")"), + new Operation.Unary("((double)", IntType.INSTANCE, ")"), + new Operation.Unary("((double)", LongType.INSTANCE, ")"), + new Operation.Unary("((double)", FloatType.INSTANCE, ")"), + // Note: There is no cast from boolean + new Operation.Unary("(-(", DoubleType.INSTANCE, "))"), new Operation.Binary("(", DoubleType.INSTANCE, " + ", DoubleType.INSTANCE, ")"), diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java index 5882588f93998..9cb2379042aa0 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java @@ -35,6 +35,13 @@ public final class FloatType extends PrimitiveType { private static final Generator GEN_FLOAT = Generators.G.floats(); private static final List OPERATIONS = List.of( + new Operation.Unary("((float)", ByteType.INSTANCE, ")"), + new Operation.Unary("((float)", CharType.INSTANCE, ")"), + new Operation.Unary("((float)", ShortType.INSTANCE, ")"), + new Operation.Unary("((float)", IntType.INSTANCE, ")"), + new Operation.Unary("((float)", LongType.INSTANCE, ")"), + new Operation.Unary("((float)", DoubleType.INSTANCE, ")"), + new Operation.Unary("(-(", FloatType.INSTANCE, "))"), new Operation.Binary("(", FloatType.INSTANCE, " + ", FloatType.INSTANCE, ")"), diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java index f318697325cb1..ddc49bc1f89a7 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java @@ -36,6 +36,8 @@ public final class IntType extends PrimitiveType { private static final List OPERATIONS = List.of( new Operation.Unary("((int)", ByteType.INSTANCE, ")"), + new Operation.Unary("((int)", CharType.INSTANCE, ")"), + new Operation.Unary("((int)", ShortType.INSTANCE, ")"), new Operation.Unary("((int)", LongType.INSTANCE, ")"), new Operation.Unary("((int)", FloatType.INSTANCE, ")"), new Operation.Unary("((int)", DoubleType.INSTANCE, ")"), @@ -56,11 +58,16 @@ public final class IntType extends PrimitiveType { new Operation.Binary("(", IntType.INSTANCE, " >> ", IntType.INSTANCE, ")"), new Operation.Binary("(", IntType.INSTANCE, " >>> ", IntType.INSTANCE, ")"), - // From Byte: new Operation.Binary("Byte.compare(", ByteType.INSTANCE, ", ", ByteType.INSTANCE, ")"), new Operation.Binary("Byte.compareUnsigned(", ByteType.INSTANCE, ", ", ByteType.INSTANCE, ")"), new Operation.Unary("Byte.toUnsignedInt(", ByteType.INSTANCE, ")"), + new Operation.Binary("Character.compare(", CharType.INSTANCE, ", ", CharType.INSTANCE, ")"), + + new Operation.Binary("Short.compare(", ShortType.INSTANCE, ", ", ShortType.INSTANCE, ")"), + new Operation.Binary("Short.compareUnsigned(", ShortType.INSTANCE, ", ", ShortType.INSTANCE, ")"), + new Operation.Unary("Short.toUnsignedInt(", ShortType.INSTANCE, ")"), + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", IntType.INSTANCE, " : ", IntType.INSTANCE, ")") ); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java index 4a9763e454bce..24a162a9332fc 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java @@ -35,6 +35,14 @@ public final class LongType extends PrimitiveType { private static final RestrictableGenerator GEN_LONG = Generators.G.longs(); private static final List OPERATIONS = List.of( + new Operation.Unary("((long)", ByteType.INSTANCE, ")"), + new Operation.Unary("((long)", CharType.INSTANCE, ")"), + new Operation.Unary("((long)", ShortType.INSTANCE, ")"), + new Operation.Unary("((long)", IntType.INSTANCE, ")"), + new Operation.Unary("((long)", FloatType.INSTANCE, ")"), + new Operation.Unary("((long)", DoubleType.INSTANCE, ")"), + // Note: There is no cast from boolean + new Operation.Unary("(-(", LongType.INSTANCE, "))"), new Operation.Unary("(~", LongType.INSTANCE, ")"), @@ -50,9 +58,10 @@ public final class LongType extends PrimitiveType { new Operation.Binary("(", LongType.INSTANCE, " >> ", LongType.INSTANCE, ")"), new Operation.Binary("(", LongType.INSTANCE, " >>> ", LongType.INSTANCE, ")"), - // From Byte: new Operation.Unary("Byte.toUnsignedLong(", ByteType.INSTANCE, ")"), + new Operation.Unary("Short.toUnsignedLong(", ShortType.INSTANCE, ")"), + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", LongType.INSTANCE, " : ", LongType.INSTANCE, ")") ); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java new file mode 100644 index 0000000000000..7446fceeb0df7 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java @@ -0,0 +1,71 @@ +/* + * 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_library.types; + +import java.util.List; + +import compiler.lib.generators.Generators; +import compiler.lib.generators.RestrictableGenerator; + +import compiler.lib.template_library.Operation; + +public final class ShortType extends PrimitiveType { + public static final ShortType INSTANCE = new ShortType(); + private static final RestrictableGenerator GEN_SHORT = Generators.G.safeRestrict(Generators.G.ints(), Short.MIN_VALUE, Short.MAX_VALUE); + + private static final List OPERATIONS = List.of( + // Note: the standard integer arithmetic operations are only defined for int/long. + // They can be used for smaller types only via automatic promotion to int, + // and then a cast back to short, e.g: + // short a = (short)(b + c) + // + // Instead of adding these operations explicitly, we just add the conversion + // from int to short, and let the IntType generate all the integer arithmetic + // operations. + new Operation.Unary("((short)", ByteType.INSTANCE, ")"), + new Operation.Unary("((short)", CharType.INSTANCE, ")"), + new Operation.Unary("((short)", IntType.INSTANCE, ")"), + new Operation.Unary("((short)", LongType.INSTANCE, ")"), + new Operation.Unary("((short)", FloatType.INSTANCE, ")"), + new Operation.Unary("((short)", DoubleType.INSTANCE, ")"), + // Note: There is no cast from boolean + + new Operation.Unary("Short.reverseBytes(", ShortType.INSTANCE, ")"), + + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", ShortType.INSTANCE, " : ", ShortType.INSTANCE, ")") + ); + + @Override + public final String name() { return "short"; } + + @Override + public final Object con() { + return "(short)" + GEN_SHORT.next(); + } + + @Override + public final List operations() { + return OPERATIONS; + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index e0bae0b6cb2c1..63b581072f0e3 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -267,12 +267,10 @@ public static String generate(CompileFramework comp) { // Use template1 100 times with every type. List templates = new ArrayList<>(); templates.add(Library.arrayFillMethods()); - for (int i = 0; i < 5; i++) { - for (Type type : Type.primitives()) { - templates.add(template1.withArgs(type)); - templates.add(template2.withArgs(type)); - templates.add(template3.withArgs(type)); - } + for (Type type : Type.primitives()) { + for (int i = 0; i < 20; i++) { templates.add(template1.withArgs(type)); } + for (int i = 0; i < 20; i++) { templates.add(template2.withArgs(type)); } + for (int i = 0; i < 5; i++) { templates.add(template3.withArgs(type)); } } return IRTestClass.TEMPLATE.withArgs(info, templates).render(); } From 46e5a0b51828a85002157780a9cf3e41daf20a9a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 26 Feb 2025 16:32:13 +0100 Subject: [PATCH 124/212] more operations --- .../template_library/types/BooleanType.java | 14 ++++++++ .../template_library/types/DoubleType.java | 5 +++ .../lib/template_library/types/FloatType.java | 7 ++++ .../lib/template_library/types/IntType.java | 36 +++++++++++++++++++ .../lib/template_library/types/LongType.java | 20 +++++++++++ .../lib/template_library/types/ShortType.java | 2 ++ .../examples/TestFuzzExpression.java | 1 - 7 files changed, 84 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java index d9a4b40db57b6..4cbeed077feb3 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java @@ -43,6 +43,20 @@ public final class BooleanType extends PrimitiveType { new Operation.Binary("(", BooleanType.INSTANCE, " && ", BooleanType.INSTANCE, ")"), new Operation.Binary("(", BooleanType.INSTANCE, " ^ ", BooleanType.INSTANCE, ")"), + new Operation.Binary("Boolean.logicalAnd(", BooleanType.INSTANCE, ", ", BooleanType.INSTANCE, ")"), + new Operation.Binary("Boolean.logicalOr(", BooleanType.INSTANCE, ", ", BooleanType.INSTANCE, ")"), + new Operation.Binary("Boolean.logicalXor(", BooleanType.INSTANCE, ", ", BooleanType.INSTANCE, ")"), + + // Note: For now, we are omitting all the Character.is<...> methods. We can add them in the future. + + new Operation.Unary("Float.isFinite(", FloatType.INSTANCE, ")"), + new Operation.Unary("Float.isInfinite(", FloatType.INSTANCE, ")"), + new Operation.Unary("Float.isNaN(", FloatType.INSTANCE, ")"), + + new Operation.Unary("Double.isFinite(", DoubleType.INSTANCE, ")"), + new Operation.Unary("Double.isInfinite(", DoubleType.INSTANCE, ")"), + new Operation.Unary("Double.isNaN(", DoubleType.INSTANCE, ")"), + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", BooleanType.INSTANCE, " : ", BooleanType.INSTANCE, ")") ); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java index 894eb9c818362..2a039f29e1ce3 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java @@ -51,6 +51,11 @@ public final class DoubleType extends PrimitiveType { new Operation.Binary("(", DoubleType.INSTANCE, " / ", DoubleType.INSTANCE, ")"), new Operation.Binary("(", DoubleType.INSTANCE, " % ", DoubleType.INSTANCE, ")"), + new Operation.Unary("Double.longBitsToDouble(", IntType.INSTANCE, ")"), + new Operation.Binary("Double.max(", DoubleType.INSTANCE, ", ", DoubleType.INSTANCE, ")"), + new Operation.Binary("Double.min(", DoubleType.INSTANCE, ", ", DoubleType.INSTANCE, ")"), + new Operation.Binary("Double.sum(", DoubleType.INSTANCE, ", ", DoubleType.INSTANCE, ")"), + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", DoubleType.INSTANCE, " : ", DoubleType.INSTANCE, ")") ); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java index 9cb2379042aa0..22bb66313d597 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java @@ -50,6 +50,13 @@ public final class FloatType extends PrimitiveType { new Operation.Binary("(", FloatType.INSTANCE, " / ", FloatType.INSTANCE, ")"), new Operation.Binary("(", FloatType.INSTANCE, " % ", FloatType.INSTANCE, ")"), + // TODO: check about float 16 type! + new Operation.Unary("Float.float16ToFloat(", ShortType.INSTANCE, ")"), + new Operation.Unary("Float.intBitsToFloat(", IntType.INSTANCE, ")"), + new Operation.Binary("Float.max(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), + new Operation.Binary("Float.min(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), + new Operation.Binary("Float.sum(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", FloatType.INSTANCE, " : ", FloatType.INSTANCE, ")") ); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java index ddc49bc1f89a7..a59d5c2d9c699 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java @@ -68,6 +68,42 @@ public final class IntType extends PrimitiveType { new Operation.Binary("Short.compareUnsigned(", ShortType.INSTANCE, ", ", ShortType.INSTANCE, ")"), new Operation.Unary("Short.toUnsignedInt(", ShortType.INSTANCE, ")"), + new Operation.Unary("Integer.bitCount(", IntType.INSTANCE, ")"), + new Operation.Binary("Integer.compare(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Binary("Integer.compareUnsigned(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Binary("Integer.compress(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Binary("Integer.divideUnsigned(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Binary("Integer.expand(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Unary("Integer.highestOneBit(", IntType.INSTANCE, ")"), + new Operation.Unary("Integer.lowestOneBit(", IntType.INSTANCE, ")"), + new Operation.Binary("Integer.min(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Binary("Integer.max(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Unary("Integer.numberOfLeadingZeros(", IntType.INSTANCE, ")"), + new Operation.Unary("Integer.numberOfTrailingZeros(", IntType.INSTANCE, ")"), + new Operation.Binary("Integer.remainderUnsigned(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Unary("Integer.reverse(", IntType.INSTANCE, ")"), + new Operation.Unary("Integer.reverseBytes(", IntType.INSTANCE, ")"), + new Operation.Binary("Integer.rotateLeft(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Binary("Integer.rotateRight(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Unary("Integer.signum(", IntType.INSTANCE, ")"), + new Operation.Binary("Integer.sum(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + + new Operation.Unary("Long.bitCount(", LongType.INSTANCE, ")"), + new Operation.Binary("Long.compare(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + new Operation.Binary("Long.compareUnsigned(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + new Operation.Unary("Long.numberOfLeadingZeros(", LongType.INSTANCE, ")"), + new Operation.Unary("Long.numberOfTrailingZeros(", LongType.INSTANCE, ")"), + new Operation.Unary("Long.signum(", LongType.INSTANCE, ")"), + + new Operation.Binary("Float.compare(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), + new Operation.Unary("Float.floatToIntBits(", FloatType.INSTANCE, ")"), + // Note: Float.floatToRawIntBits can lead to issues, because the NaN values are not always + // represented by the same int bits. + + new Operation.Binary("Double.compare(", DoubleType.INSTANCE, ", ", DoubleType.INSTANCE, ")"), + + new Operation.Binary("Boolean.compare(", BooleanType.INSTANCE, ", ", BooleanType.INSTANCE, ")"), + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", IntType.INSTANCE, " : ", IntType.INSTANCE, ")") ); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java index 24a162a9332fc..fd4495d7f7880 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java @@ -62,6 +62,26 @@ public final class LongType extends PrimitiveType { new Operation.Unary("Short.toUnsignedLong(", ShortType.INSTANCE, ")"), + new Operation.Unary("Integer.toUnsignedLong(", IntType.INSTANCE, ")"), + + new Operation.Binary("Long.compress(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + new Operation.Binary("Long.divideUnsigned(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + new Operation.Binary("Long.expand(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + new Operation.Unary("Long.highestOneBit(", LongType.INSTANCE, ")"), + new Operation.Unary("Long.lowestOneBit(", LongType.INSTANCE, ")"), + new Operation.Binary("Long.min(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + new Operation.Binary("Long.max(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + new Operation.Binary("Long.remainderUnsigned(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + new Operation.Unary("Long.reverse(", LongType.INSTANCE, ")"), + new Operation.Unary("Long.reverseBytes(", LongType.INSTANCE, ")"), + new Operation.Binary("Long.rotateLeft(", LongType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Binary("Long.rotateRight(", LongType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Binary("Long.sum(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + + new Operation.Unary("Double.doubleToLongBits(", DoubleType.INSTANCE, ")"), + // Note: Double.doubleToRawLongBits can lead to issues, because the NaN values are not always + // represented by the same long bits. + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", LongType.INSTANCE, " : ", LongType.INSTANCE, ")") ); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java index 7446fceeb0df7..c822b072a3314 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java @@ -53,6 +53,8 @@ public final class ShortType extends PrimitiveType { new Operation.Unary("Short.reverseBytes(", ShortType.INSTANCE, ")"), + new Operation.Unary("Float.floatToFloat16(", FloatType.INSTANCE, ")"), + new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", ShortType.INSTANCE, " : ", ShortType.INSTANCE, ")") ); diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index 63b581072f0e3..ed1ab62747cb9 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -263,7 +263,6 @@ public static String generate(CompileFramework comp) { // TODO: hand-unrollling case - // Use template1 100 times with every type. List templates = new ArrayList<>(); templates.add(Library.arrayFillMethods()); From d7615126849864fb2a87f4c147dc76e0cc158659 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 27 Feb 2025 08:21:06 +0100 Subject: [PATCH 125/212] rm Float.floatToFloat16 because of NaN issues --- .../jtreg/compiler/lib/template_library/types/FloatType.java | 1 - .../jtreg/compiler/lib/template_library/types/ShortType.java | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java index 22bb66313d597..857ae4f80526b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java @@ -50,7 +50,6 @@ public final class FloatType extends PrimitiveType { new Operation.Binary("(", FloatType.INSTANCE, " / ", FloatType.INSTANCE, ")"), new Operation.Binary("(", FloatType.INSTANCE, " % ", FloatType.INSTANCE, ")"), - // TODO: check about float 16 type! new Operation.Unary("Float.float16ToFloat(", ShortType.INSTANCE, ")"), new Operation.Unary("Float.intBitsToFloat(", IntType.INSTANCE, ")"), new Operation.Binary("Float.max(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java index c822b072a3314..368eb3b509959 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java @@ -53,7 +53,8 @@ public final class ShortType extends PrimitiveType { new Operation.Unary("Short.reverseBytes(", ShortType.INSTANCE, ")"), - new Operation.Unary("Float.floatToFloat16(", FloatType.INSTANCE, ")"), + // Note: Float.floatToFloat16 can lead to issues, because NaN values are not always + // represented by the same short bits. new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", ShortType.INSTANCE, " : ", ShortType.INSTANCE, ")") ); From ff0478d4d4a48816049ac13cf7a2373bfeb1ab91 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 27 Feb 2025 13:03:59 +0100 Subject: [PATCH 126/212] operation refactor --- .../lib/template_library/Expression.java | 8 +- .../lib/template_library/Operation.java | 34 +- .../lib/template_library/Operations.java | 317 ++++++++++++++++++ .../compiler/lib/template_library/Type.java | 2 - .../template_library/types/BooleanType.java | 35 -- .../lib/template_library/types/ByteType.java | 29 -- .../lib/template_library/types/CharType.java | 31 -- .../template_library/types/DoubleType.java | 34 -- .../lib/template_library/types/FloatType.java | 34 -- .../lib/template_library/types/IntType.java | 82 ----- .../lib/template_library/types/LongType.java | 60 ---- .../lib/template_library/types/ShortType.java | 34 -- 12 files changed, 342 insertions(+), 358 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/Operations.java diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index a0c193a73e134..253e1aa51f5fb 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -107,7 +107,7 @@ private static final ExpressionGenerator expressionGenerator(Type resultType, Ha } private static final ExpressionGeneratorStep expressionGeneratorStep(Type resultType, HashSet allowedTypes, int maxDepth, List types) { - List ops = resultType.operations().stream().filter(o -> o.hasOnlyTypes(allowedTypes)).toList(); + List ops = Operations.PRIMITIVE_OPERATIONS.stream().filter(o -> o.matchesTypes(resultType, allowedTypes)).toList(); if (maxDepth <= 0 || ops.isEmpty() || RANDOM.nextInt(2 * maxDepth) == 0) { // Remember which type we need to fill the ith argument with. int i = types.size(); @@ -118,7 +118,7 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result }; } switch (Library.choice(ops)) { - case Operation.Unary(String s0, Type t0, String s1) -> { + case Operation.Unary(Type r, String s0, Type t0, String s1) -> { ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, allowedTypes, maxDepth-1, types); return (List tokens, List args) -> { tokens.add(s0); @@ -126,7 +126,7 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result tokens.add(s1); }; } - case Operation.Binary(String s0, Type t0, String s1, Type t1, String s2) -> { + case Operation.Binary(Type r, String s0, Type t0, String s1, Type t1, String s2) -> { ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, allowedTypes, maxDepth-1, types); ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, allowedTypes, maxDepth-1, types); return (List tokens, List args) -> { @@ -137,7 +137,7 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result tokens.add(s2); }; } - case Operation.Ternary(String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3) -> { + case Operation.Ternary(Type r, String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3) -> { ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, allowedTypes, maxDepth-1, types); ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, allowedTypes, maxDepth-1, types); ExpressionGeneratorStep step2 = expressionGeneratorStep(t1, allowedTypes, maxDepth-1, types); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operation.java b/test/hotspot/jtreg/compiler/lib/template_library/Operation.java index 50e8476bc2229..191df6c8a5e8a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operation.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operation.java @@ -28,29 +28,37 @@ public sealed interface Operation permits Operation.Unary, Operation.Binary, Operation.Ternary { - boolean hasOnlyTypes(HashSet types); - public static record Unary(String s0, Type t0, String s1) implements Operation { + /** + * Check if input types are in the set. + * Check that return type is subtype of the desired type. + */ + public abstract boolean matchesTypes(Type returnType, HashSet argumentTypes); + + public static record Unary(Type r, String s0, Type t0, String s1) implements Operation { @Override - public boolean hasOnlyTypes(HashSet types) { - return types.contains(t0); + public boolean matchesTypes(Type returnType, HashSet argumentTypes) { + return r.isSubtypeOf(returnType) && + argumentTypes.contains(t0); } } - public static record Binary(String s0, Type t0, String s1, Type t1, String s2) implements Operation { + public static record Binary(Type r, String s0, Type t0, String s1, Type t1, String s2) implements Operation { @Override - public boolean hasOnlyTypes(HashSet types) { - return types.contains(t0) && - types.contains(t1); + public boolean matchesTypes(Type returnType, HashSet argumentTypes) { + return r.isSubtypeOf(returnType) && + argumentTypes.contains(t0) && + argumentTypes.contains(t1); } } - public static record Ternary(String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3) implements Operation { + public static record Ternary(Type r, String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3) implements Operation { @Override - public boolean hasOnlyTypes(HashSet types) { - return types.contains(t0) && - types.contains(t1) && - types.contains(t2); + public boolean matchesTypes(Type returnType, HashSet argumentTypes) { + return r.isSubtypeOf(returnType) && + argumentTypes.contains(t0) && + argumentTypes.contains(t1) && + argumentTypes.contains(t2); } } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java new file mode 100644 index 0000000000000..6eb915149cb48 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -0,0 +1,317 @@ +/* + * 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_library; + +import java.util.List; +import java.util.stream.Stream; + +import compiler.lib.template_library.types.ByteType; +import compiler.lib.template_library.types.CharType; +import compiler.lib.template_library.types.ShortType; +import compiler.lib.template_library.types.IntType; +import compiler.lib.template_library.types.LongType; +import compiler.lib.template_library.types.FloatType; +import compiler.lib.template_library.types.DoubleType; +import compiler.lib.template_library.types.BooleanType; + +public class Operations { + + private static final List BYTE_OPERATIONS = List.of( + // Note: the standard integer arithmetic operations are only defined for int/long. + // They can be used for smaller types only via automatic promotion to int, + // and then a cast back to byte, e.g: + // byte a = (byte)(b + c) + // + // Instead of adding these operations explicitly, we just add the conversion + // from int to byte, and let the IntType generate all the integer arithmetic + // operations. + new Operation.Unary(ByteType.INSTANCE, "((byte)", CharType.INSTANCE, ")"), + new Operation.Unary(ByteType.INSTANCE, "((byte)", ShortType.INSTANCE, ")"), + new Operation.Unary(ByteType.INSTANCE, "((byte)", IntType.INSTANCE, ")"), + new Operation.Unary(ByteType.INSTANCE, "((byte)", LongType.INSTANCE, ")"), + new Operation.Unary(ByteType.INSTANCE, "((byte)", FloatType.INSTANCE, ")"), + new Operation.Unary(ByteType.INSTANCE, "((byte)", DoubleType.INSTANCE, ")"), + // Note: There is no cast from boolean + + new Operation.Ternary(ByteType.INSTANCE, "(", BooleanType.INSTANCE, " ? ", ByteType.INSTANCE, " : ", ByteType.INSTANCE, ")") + ); + + private static final List CHAR_OPERATIONS = List.of( + // Note: the standard integer arithmetic operations are only defined for int/long. + // They can be used for smaller types only via automatic promotion to int, + // and then a cast back to char, e.g: + // char a = (char)(b + c) + // + // Instead of adding these operations explicitly, we just add the conversion + // from int to char, and let the IntType generate all the integer arithmetic + // operations. + new Operation.Unary(CharType.INSTANCE, "((char)", ByteType.INSTANCE, ")"), + new Operation.Unary(CharType.INSTANCE, "((char)", ShortType.INSTANCE, ")"), + new Operation.Unary(CharType.INSTANCE, "((char)", IntType.INSTANCE, ")"), + new Operation.Unary(CharType.INSTANCE, "((char)", LongType.INSTANCE, ")"), + new Operation.Unary(CharType.INSTANCE, "((char)", FloatType.INSTANCE, ")"), + new Operation.Unary(CharType.INSTANCE, "((char)", DoubleType.INSTANCE, ")"), + // Note: There is no cast from boolean + + new Operation.Unary(CharType.INSTANCE, "Character.reverseBytes(", CharType.INSTANCE, ")"), + + new Operation.Ternary(CharType.INSTANCE, "(", BooleanType.INSTANCE, " ? ", CharType.INSTANCE, " : ", CharType.INSTANCE, ")") + ); + + private static final List SHORT_OPERATIONS = List.of( + // Note: the standard integer arithmetic operations are only defined for int/long. + // They can be used for smaller types only via automatic promotion to int, + // and then a cast back to short, e.g: + // short a = (short)(b + c) + // + // Instead of adding these operations explicitly, we just add the conversion + // from int to short, and let the IntType generate all the integer arithmetic + // operations. + new Operation.Unary(ShortType.INSTANCE, "((short)", ByteType.INSTANCE, ")"), + new Operation.Unary(ShortType.INSTANCE, "((short)", CharType.INSTANCE, ")"), + new Operation.Unary(ShortType.INSTANCE, "((short)", IntType.INSTANCE, ")"), + new Operation.Unary(ShortType.INSTANCE, "((short)", LongType.INSTANCE, ")"), + new Operation.Unary(ShortType.INSTANCE, "((short)", FloatType.INSTANCE, ")"), + new Operation.Unary(ShortType.INSTANCE, "((short)", DoubleType.INSTANCE, ")"), + // Note: There is no cast from boolean + + new Operation.Unary(ShortType.INSTANCE, "Short.reverseBytes(", ShortType.INSTANCE, ")"), + + // Note: Float.floatToFloat16 can lead to issues, because NaN values are not always + // represented by the same short bits. + + new Operation.Ternary(ShortType.INSTANCE, "(", BooleanType.INSTANCE, " ? ", ShortType.INSTANCE, " : ", ShortType.INSTANCE, ")") + ); + + private static final List INT_OPERATIONS = List.of( + new Operation.Unary(IntType.INSTANCE, "((int)", ByteType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "((int)", CharType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "((int)", ShortType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "((int)", LongType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "((int)", FloatType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "((int)", DoubleType.INSTANCE, ")"), + // Note: There is no cast from boolean + + new Operation.Unary(IntType.INSTANCE, "(-(", IntType.INSTANCE, "))"), + new Operation.Unary(IntType.INSTANCE, "(~", IntType.INSTANCE, ")"), + + new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " + ", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " - ", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " * ", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " / ", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " % ", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " & ", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " | ", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " ^ ", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " << ", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " >> ", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " >>> ", IntType.INSTANCE, ")"), + + new Operation.Binary(IntType.INSTANCE, "Byte.compare(", ByteType.INSTANCE, ", ", ByteType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "Byte.compareUnsigned(", ByteType.INSTANCE, ", ", ByteType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "Byte.toUnsignedInt(", ByteType.INSTANCE, ")"), + + new Operation.Binary(IntType.INSTANCE, "Character.compare(", CharType.INSTANCE, ", ", CharType.INSTANCE, ")"), + + new Operation.Binary(IntType.INSTANCE, "Short.compare(", ShortType.INSTANCE, ", ", ShortType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "Short.compareUnsigned(", ShortType.INSTANCE, ", ", ShortType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "Short.toUnsignedInt(", ShortType.INSTANCE, ")"), + + new Operation.Unary(IntType.INSTANCE, "Integer.bitCount(", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "Integer.compare(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "Integer.compareUnsigned(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "Integer.compress(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "Integer.divideUnsigned(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "Integer.expand(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "Integer.highestOneBit(", IntType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "Integer.lowestOneBit(", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "Integer.min(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "Integer.max(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "Integer.numberOfLeadingZeros(", IntType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "Integer.numberOfTrailingZeros(", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "Integer.remainderUnsigned(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "Integer.reverse(", IntType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "Integer.reverseBytes(", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "Integer.rotateLeft(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "Integer.rotateRight(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "Integer.signum(", IntType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "Integer.sum(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), + + new Operation.Unary(IntType.INSTANCE, "Long.bitCount(", LongType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "Long.compare(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + new Operation.Binary(IntType.INSTANCE, "Long.compareUnsigned(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "Long.numberOfLeadingZeros(", LongType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "Long.numberOfTrailingZeros(", LongType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "Long.signum(", LongType.INSTANCE, ")"), + + new Operation.Binary(IntType.INSTANCE, "Float.compare(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), + new Operation.Unary(IntType.INSTANCE, "Float.floatToIntBits(", FloatType.INSTANCE, ")"), + // Note: Float.floatToRawIntBits can lead to issues, because the NaN values are not always + // represented by the same int bits. + + new Operation.Binary(IntType.INSTANCE, "Double.compare(", DoubleType.INSTANCE, ", ", DoubleType.INSTANCE, ")"), + + new Operation.Binary(IntType.INSTANCE, "Boolean.compare(", BooleanType.INSTANCE, ", ", BooleanType.INSTANCE, ")"), + + new Operation.Ternary(IntType.INSTANCE, "(", BooleanType.INSTANCE, " ? ", IntType.INSTANCE, " : ", IntType.INSTANCE, ")") + ); + + private static final List LONG_OPERATIONS = List.of( + new Operation.Unary(LongType.INSTANCE, "((long)", ByteType.INSTANCE, ")"), + new Operation.Unary(LongType.INSTANCE, "((long)", CharType.INSTANCE, ")"), + new Operation.Unary(LongType.INSTANCE, "((long)", ShortType.INSTANCE, ")"), + new Operation.Unary(LongType.INSTANCE, "((long)", IntType.INSTANCE, ")"), + new Operation.Unary(LongType.INSTANCE, "((long)", FloatType.INSTANCE, ")"), + new Operation.Unary(LongType.INSTANCE, "((long)", DoubleType.INSTANCE, ")"), + // Note: There is no cast from boolean + + new Operation.Unary(LongType.INSTANCE, "(-(", LongType.INSTANCE, "))"), + new Operation.Unary(LongType.INSTANCE, "(~", LongType.INSTANCE, ")"), + + new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " + ", LongType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " - ", LongType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " * ", LongType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " / ", LongType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " % ", LongType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " & ", LongType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " | ", LongType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " ^ ", LongType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " << ", LongType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " >> ", LongType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " >>> ", LongType.INSTANCE, ")"), + + new Operation.Unary(LongType.INSTANCE, "Byte.toUnsignedLong(", ByteType.INSTANCE, ")"), + + new Operation.Unary(LongType.INSTANCE, "Short.toUnsignedLong(", ShortType.INSTANCE, ")"), + + new Operation.Unary(LongType.INSTANCE, "Integer.toUnsignedLong(", IntType.INSTANCE, ")"), + + new Operation.Binary(LongType.INSTANCE, "Long.compress(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "Long.divideUnsigned(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "Long.expand(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + new Operation.Unary(LongType.INSTANCE, "Long.highestOneBit(", LongType.INSTANCE, ")"), + new Operation.Unary(LongType.INSTANCE, "Long.lowestOneBit(", LongType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "Long.min(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "Long.max(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "Long.remainderUnsigned(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + new Operation.Unary(LongType.INSTANCE, "Long.reverse(", LongType.INSTANCE, ")"), + new Operation.Unary(LongType.INSTANCE, "Long.reverseBytes(", LongType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "Long.rotateLeft(", LongType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "Long.rotateRight(", LongType.INSTANCE, ", ", IntType.INSTANCE, ")"), + new Operation.Binary(LongType.INSTANCE, "Long.sum(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), + + new Operation.Unary(LongType.INSTANCE, "Double.doubleToLongBits(", DoubleType.INSTANCE, ")"), + // Note: Double.doubleToRawLongBits can lead to issues, because the NaN values are not always + // represented by the same long bits. + + new Operation.Ternary(LongType.INSTANCE, "(", BooleanType.INSTANCE, " ? ", LongType.INSTANCE, " : ", LongType.INSTANCE, ")") + ); + + private static final List FLOAT_OPERATIONS = List.of( + new Operation.Unary(FloatType.INSTANCE, "((float)", ByteType.INSTANCE, ")"), + new Operation.Unary(FloatType.INSTANCE, "((float)", CharType.INSTANCE, ")"), + new Operation.Unary(FloatType.INSTANCE, "((float)", ShortType.INSTANCE, ")"), + new Operation.Unary(FloatType.INSTANCE, "((float)", IntType.INSTANCE, ")"), + new Operation.Unary(FloatType.INSTANCE, "((float)", LongType.INSTANCE, ")"), + new Operation.Unary(FloatType.INSTANCE, "((float)", DoubleType.INSTANCE, ")"), + + new Operation.Unary(FloatType.INSTANCE, "(-(", FloatType.INSTANCE, "))"), + + new Operation.Binary(FloatType.INSTANCE, "(", FloatType.INSTANCE, " + ", FloatType.INSTANCE, ")"), + new Operation.Binary(FloatType.INSTANCE, "(", FloatType.INSTANCE, " - ", FloatType.INSTANCE, ")"), + new Operation.Binary(FloatType.INSTANCE, "(", FloatType.INSTANCE, " * ", FloatType.INSTANCE, ")"), + new Operation.Binary(FloatType.INSTANCE, "(", FloatType.INSTANCE, " / ", FloatType.INSTANCE, ")"), + new Operation.Binary(FloatType.INSTANCE, "(", FloatType.INSTANCE, " % ", FloatType.INSTANCE, ")"), + + new Operation.Unary(FloatType.INSTANCE, "Float.float16ToFloat(", ShortType.INSTANCE, ")"), + new Operation.Unary(FloatType.INSTANCE, "Float.intBitsToFloat(", IntType.INSTANCE, ")"), + new Operation.Binary(FloatType.INSTANCE, "Float.max(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), + new Operation.Binary(FloatType.INSTANCE, "Float.min(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), + new Operation.Binary(FloatType.INSTANCE, "Float.sum(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), + + new Operation.Ternary(FloatType.INSTANCE, "(", BooleanType.INSTANCE, " ? ", FloatType.INSTANCE, " : ", FloatType.INSTANCE, ")") + ); + + private static final List DOUBLE_OPERATIONS = List.of( + new Operation.Unary(DoubleType.INSTANCE, "((double)", ByteType.INSTANCE, ")"), + new Operation.Unary(DoubleType.INSTANCE, "((double)", CharType.INSTANCE, ")"), + new Operation.Unary(DoubleType.INSTANCE, "((double)", ShortType.INSTANCE, ")"), + new Operation.Unary(DoubleType.INSTANCE, "((double)", IntType.INSTANCE, ")"), + new Operation.Unary(DoubleType.INSTANCE, "((double)", LongType.INSTANCE, ")"), + new Operation.Unary(DoubleType.INSTANCE, "((double)", FloatType.INSTANCE, ")"), + // Note: There is no cast from boolean + + new Operation.Unary(DoubleType.INSTANCE, "(-(", DoubleType.INSTANCE, "))"), + + new Operation.Binary(DoubleType.INSTANCE, "(", DoubleType.INSTANCE, " + ", DoubleType.INSTANCE, ")"), + new Operation.Binary(DoubleType.INSTANCE, "(", DoubleType.INSTANCE, " - ", DoubleType.INSTANCE, ")"), + new Operation.Binary(DoubleType.INSTANCE, "(", DoubleType.INSTANCE, " * ", DoubleType.INSTANCE, ")"), + new Operation.Binary(DoubleType.INSTANCE, "(", DoubleType.INSTANCE, " / ", DoubleType.INSTANCE, ")"), + new Operation.Binary(DoubleType.INSTANCE, "(", DoubleType.INSTANCE, " % ", DoubleType.INSTANCE, ")"), + + new Operation.Unary(DoubleType.INSTANCE, "Double.longBitsToDouble(", IntType.INSTANCE, ")"), + new Operation.Binary(DoubleType.INSTANCE, "Double.max(", DoubleType.INSTANCE, ", ", DoubleType.INSTANCE, ")"), + new Operation.Binary(DoubleType.INSTANCE, "Double.min(", DoubleType.INSTANCE, ", ", DoubleType.INSTANCE, ")"), + new Operation.Binary(DoubleType.INSTANCE, "Double.sum(", DoubleType.INSTANCE, ", ", DoubleType.INSTANCE, ")"), + + new Operation.Ternary(DoubleType.INSTANCE, "(", BooleanType.INSTANCE, " ? ", DoubleType.INSTANCE, " : ", DoubleType.INSTANCE, ")") + ); + + private static final List BOOLEAN_OPERATIONS = List.of( + // Note: there is no casting / conversion from an to boolean directly. + + new Operation.Unary(BooleanType.INSTANCE, "(!(", BooleanType.INSTANCE, "))"), + + new Operation.Binary(BooleanType.INSTANCE, "(", BooleanType.INSTANCE, " || ", BooleanType.INSTANCE, ")"), + new Operation.Binary(BooleanType.INSTANCE, "(", BooleanType.INSTANCE, " && ", BooleanType.INSTANCE, ")"), + new Operation.Binary(BooleanType.INSTANCE, "(", BooleanType.INSTANCE, " ^ ", BooleanType.INSTANCE, ")"), + + new Operation.Binary(BooleanType.INSTANCE, "Boolean.logicalAnd(", BooleanType.INSTANCE, ", ", BooleanType.INSTANCE, ")"), + new Operation.Binary(BooleanType.INSTANCE, "Boolean.logicalOr(", BooleanType.INSTANCE, ", ", BooleanType.INSTANCE, ")"), + new Operation.Binary(BooleanType.INSTANCE, "Boolean.logicalXor(", BooleanType.INSTANCE, ", ", BooleanType.INSTANCE, ")"), + + // Note: For now, we are omitting all the Character.is<...> methods. We can add them in the future. + + new Operation.Unary(BooleanType.INSTANCE, "Float.isFinite(", FloatType.INSTANCE, ")"), + new Operation.Unary(BooleanType.INSTANCE, "Float.isInfinite(", FloatType.INSTANCE, ")"), + new Operation.Unary(BooleanType.INSTANCE, "Float.isNaN(", FloatType.INSTANCE, ")"), + + new Operation.Unary(BooleanType.INSTANCE, "Double.isFinite(", DoubleType.INSTANCE, ")"), + new Operation.Unary(BooleanType.INSTANCE, "Double.isInfinite(", DoubleType.INSTANCE, ")"), + new Operation.Unary(BooleanType.INSTANCE, "Double.isNaN(", DoubleType.INSTANCE, ")"), + + new Operation.Ternary(BooleanType.INSTANCE, "(", BooleanType.INSTANCE, " ? ", BooleanType.INSTANCE, " : ", BooleanType.INSTANCE, ")") + ); + + public static final List PRIMITIVE_OPERATIONS = Stream.of( + BYTE_OPERATIONS, + CHAR_OPERATIONS, + SHORT_OPERATIONS, + INT_OPERATIONS, + LONG_OPERATIONS, + FLOAT_OPERATIONS, + DOUBLE_OPERATIONS, + BOOLEAN_OPERATIONS + ).flatMap((List l) -> l.stream()).toList(); +} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Type.java b/test/hotspot/jtreg/compiler/lib/template_library/Type.java index 4784833f94298..b3ab8adab7ea5 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Type.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Type.java @@ -68,6 +68,4 @@ public static final List primitives() { public final String toString() { return name(); } public abstract Object con(); - - public abstract List operations(); } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java index 4cbeed077feb3..85ac74378d6c4 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java @@ -23,43 +23,13 @@ package compiler.lib.template_library.types; -import java.util.List; - import java.util.Random; import jdk.test.lib.Utils; -import compiler.lib.template_library.Operation; - public final class BooleanType extends PrimitiveType { public static final BooleanType INSTANCE = new BooleanType(); private static final Random RANDOM = Utils.getRandomInstance(); - private static final List OPERATIONS = List.of( - // Note: there is no casting / conversion from an to boolean directly. - - new Operation.Unary("(!(", BooleanType.INSTANCE, "))"), - - new Operation.Binary("(", BooleanType.INSTANCE, " || ", BooleanType.INSTANCE, ")"), - new Operation.Binary("(", BooleanType.INSTANCE, " && ", BooleanType.INSTANCE, ")"), - new Operation.Binary("(", BooleanType.INSTANCE, " ^ ", BooleanType.INSTANCE, ")"), - - new Operation.Binary("Boolean.logicalAnd(", BooleanType.INSTANCE, ", ", BooleanType.INSTANCE, ")"), - new Operation.Binary("Boolean.logicalOr(", BooleanType.INSTANCE, ", ", BooleanType.INSTANCE, ")"), - new Operation.Binary("Boolean.logicalXor(", BooleanType.INSTANCE, ", ", BooleanType.INSTANCE, ")"), - - // Note: For now, we are omitting all the Character.is<...> methods. We can add them in the future. - - new Operation.Unary("Float.isFinite(", FloatType.INSTANCE, ")"), - new Operation.Unary("Float.isInfinite(", FloatType.INSTANCE, ")"), - new Operation.Unary("Float.isNaN(", FloatType.INSTANCE, ")"), - - new Operation.Unary("Double.isFinite(", DoubleType.INSTANCE, ")"), - new Operation.Unary("Double.isInfinite(", DoubleType.INSTANCE, ")"), - new Operation.Unary("Double.isNaN(", DoubleType.INSTANCE, ")"), - - new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", BooleanType.INSTANCE, " : ", BooleanType.INSTANCE, ")") - ); - @Override public final String name() { return "boolean"; } @@ -68,9 +38,4 @@ public final Object con() { // TODO: generator for boolean? Could have different probabilities! return RANDOM.nextInt() % 2 == 0; } - - @Override - public final List operations() { - return OPERATIONS; - } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/ByteType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/ByteType.java index 22b3e36ba258a..3a70609433900 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/ByteType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/ByteType.java @@ -23,37 +23,13 @@ package compiler.lib.template_library.types; -import java.util.List; - import compiler.lib.generators.Generators; import compiler.lib.generators.RestrictableGenerator; -import compiler.lib.template_library.Operation; - public final class ByteType extends PrimitiveType { public static final ByteType INSTANCE = new ByteType(); private static final RestrictableGenerator GEN_BYTE = Generators.G.safeRestrict(Generators.G.ints(), Byte.MIN_VALUE, Byte.MAX_VALUE); - private static final List OPERATIONS = List.of( - // Note: the standard integer arithmetic operations are only defined for int/long. - // They can be used for smaller types only via automatic promotion to int, - // and then a cast back to byte, e.g: - // byte a = (byte)(b + c) - // - // Instead of adding these operations explicitly, we just add the conversion - // from int to byte, and let the IntType generate all the integer arithmetic - // operations. - new Operation.Unary("((byte)", CharType.INSTANCE, ")"), - new Operation.Unary("((byte)", ShortType.INSTANCE, ")"), - new Operation.Unary("((byte)", IntType.INSTANCE, ")"), - new Operation.Unary("((byte)", LongType.INSTANCE, ")"), - new Operation.Unary("((byte)", FloatType.INSTANCE, ")"), - new Operation.Unary("((byte)", DoubleType.INSTANCE, ")"), - // Note: There is no cast from boolean - - new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", ByteType.INSTANCE, " : ", ByteType.INSTANCE, ")") - ); - @Override public final String name() { return "byte"; } @@ -61,9 +37,4 @@ public final class ByteType extends PrimitiveType { public final Object con() { return "(byte)" + GEN_BYTE.next(); } - - @Override - public final List operations() { - return OPERATIONS; - } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/CharType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/CharType.java index 427ef97b5249f..598eeaac98721 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/CharType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/CharType.java @@ -23,39 +23,13 @@ package compiler.lib.template_library.types; -import java.util.List; - import compiler.lib.generators.Generators; import compiler.lib.generators.RestrictableGenerator; -import compiler.lib.template_library.Operation; - public final class CharType extends PrimitiveType { public static final CharType INSTANCE = new CharType(); private static final RestrictableGenerator GEN_CHAR = Generators.G.safeRestrict(Generators.G.ints(), Character.MIN_VALUE, Character.MAX_VALUE); - private static final List OPERATIONS = List.of( - // Note: the standard integer arithmetic operations are only defined for int/long. - // They can be used for smaller types only via automatic promotion to int, - // and then a cast back to char, e.g: - // char a = (char)(b + c) - // - // Instead of adding these operations explicitly, we just add the conversion - // from int to char, and let the IntType generate all the integer arithmetic - // operations. - new Operation.Unary("((char)", ByteType.INSTANCE, ")"), - new Operation.Unary("((char)", ShortType.INSTANCE, ")"), - new Operation.Unary("((char)", IntType.INSTANCE, ")"), - new Operation.Unary("((char)", LongType.INSTANCE, ")"), - new Operation.Unary("((char)", FloatType.INSTANCE, ")"), - new Operation.Unary("((char)", DoubleType.INSTANCE, ")"), - // Note: There is no cast from boolean - - new Operation.Unary("Character.reverseBytes(", CharType.INSTANCE, ")"), - - new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", CharType.INSTANCE, " : ", CharType.INSTANCE, ")") - ); - @Override public final String name() { return "char"; } @@ -63,9 +37,4 @@ public final class CharType extends PrimitiveType { public final Object con() { return "(char)" + GEN_CHAR.next(); } - - @Override - public final List operations() { - return OPERATIONS; - } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java index 2a039f29e1ce3..f91894ef54d31 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java @@ -23,42 +23,13 @@ package compiler.lib.template_library.types; -import java.util.List; - import compiler.lib.generators.Generators; import compiler.lib.generators.Generator; -import compiler.lib.template_library.Operation; - public final class DoubleType extends PrimitiveType { public static final DoubleType INSTANCE = new DoubleType(); private static final Generator GEN_DOUBLE = Generators.G.doubles(); - private static final List OPERATIONS = List.of( - new Operation.Unary("((double)", ByteType.INSTANCE, ")"), - new Operation.Unary("((double)", CharType.INSTANCE, ")"), - new Operation.Unary("((double)", ShortType.INSTANCE, ")"), - new Operation.Unary("((double)", IntType.INSTANCE, ")"), - new Operation.Unary("((double)", LongType.INSTANCE, ")"), - new Operation.Unary("((double)", FloatType.INSTANCE, ")"), - // Note: There is no cast from boolean - - new Operation.Unary("(-(", DoubleType.INSTANCE, "))"), - - new Operation.Binary("(", DoubleType.INSTANCE, " + ", DoubleType.INSTANCE, ")"), - new Operation.Binary("(", DoubleType.INSTANCE, " - ", DoubleType.INSTANCE, ")"), - new Operation.Binary("(", DoubleType.INSTANCE, " * ", DoubleType.INSTANCE, ")"), - new Operation.Binary("(", DoubleType.INSTANCE, " / ", DoubleType.INSTANCE, ")"), - new Operation.Binary("(", DoubleType.INSTANCE, " % ", DoubleType.INSTANCE, ")"), - - new Operation.Unary("Double.longBitsToDouble(", IntType.INSTANCE, ")"), - new Operation.Binary("Double.max(", DoubleType.INSTANCE, ", ", DoubleType.INSTANCE, ")"), - new Operation.Binary("Double.min(", DoubleType.INSTANCE, ", ", DoubleType.INSTANCE, ")"), - new Operation.Binary("Double.sum(", DoubleType.INSTANCE, ", ", DoubleType.INSTANCE, ")"), - - new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", DoubleType.INSTANCE, " : ", DoubleType.INSTANCE, ")") - ); - @Override public final String name() { return "double"; } @@ -66,9 +37,4 @@ public final class DoubleType extends PrimitiveType { public final Object con() { return GEN_DOUBLE.next(); } - - @Override - public final List operations() { - return OPERATIONS; - } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java index 857ae4f80526b..bec1b3f55d8fe 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java @@ -23,42 +23,13 @@ package compiler.lib.template_library.types; -import java.util.List; - import compiler.lib.generators.Generators; import compiler.lib.generators.Generator; -import compiler.lib.template_library.Operation; - public final class FloatType extends PrimitiveType { public static final FloatType INSTANCE = new FloatType(); private static final Generator GEN_FLOAT = Generators.G.floats(); - private static final List OPERATIONS = List.of( - new Operation.Unary("((float)", ByteType.INSTANCE, ")"), - new Operation.Unary("((float)", CharType.INSTANCE, ")"), - new Operation.Unary("((float)", ShortType.INSTANCE, ")"), - new Operation.Unary("((float)", IntType.INSTANCE, ")"), - new Operation.Unary("((float)", LongType.INSTANCE, ")"), - new Operation.Unary("((float)", DoubleType.INSTANCE, ")"), - - new Operation.Unary("(-(", FloatType.INSTANCE, "))"), - - new Operation.Binary("(", FloatType.INSTANCE, " + ", FloatType.INSTANCE, ")"), - new Operation.Binary("(", FloatType.INSTANCE, " - ", FloatType.INSTANCE, ")"), - new Operation.Binary("(", FloatType.INSTANCE, " * ", FloatType.INSTANCE, ")"), - new Operation.Binary("(", FloatType.INSTANCE, " / ", FloatType.INSTANCE, ")"), - new Operation.Binary("(", FloatType.INSTANCE, " % ", FloatType.INSTANCE, ")"), - - new Operation.Unary("Float.float16ToFloat(", ShortType.INSTANCE, ")"), - new Operation.Unary("Float.intBitsToFloat(", IntType.INSTANCE, ")"), - new Operation.Binary("Float.max(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), - new Operation.Binary("Float.min(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), - new Operation.Binary("Float.sum(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), - - new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", FloatType.INSTANCE, " : ", FloatType.INSTANCE, ")") - ); - @Override public final String name() { return "float"; } @@ -66,9 +37,4 @@ public final class FloatType extends PrimitiveType { public final Object con() { return GEN_FLOAT.next(); } - - @Override - public final List operations() { - return OPERATIONS; - } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java index a59d5c2d9c699..5462fd9784700 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java @@ -23,90 +23,13 @@ package compiler.lib.template_library.types; -import java.util.List; - import compiler.lib.generators.Generators; import compiler.lib.generators.RestrictableGenerator; -import compiler.lib.template_library.Operation; - public final class IntType extends PrimitiveType { public static final IntType INSTANCE = new IntType(); private static final RestrictableGenerator GEN_INT = Generators.G.ints(); - private static final List OPERATIONS = List.of( - new Operation.Unary("((int)", ByteType.INSTANCE, ")"), - new Operation.Unary("((int)", CharType.INSTANCE, ")"), - new Operation.Unary("((int)", ShortType.INSTANCE, ")"), - new Operation.Unary("((int)", LongType.INSTANCE, ")"), - new Operation.Unary("((int)", FloatType.INSTANCE, ")"), - new Operation.Unary("((int)", DoubleType.INSTANCE, ")"), - // Note: There is no cast from boolean - - new Operation.Unary("(-(", IntType.INSTANCE, "))"), - new Operation.Unary("(~", IntType.INSTANCE, ")"), - - new Operation.Binary("(", IntType.INSTANCE, " + ", IntType.INSTANCE, ")"), - new Operation.Binary("(", IntType.INSTANCE, " - ", IntType.INSTANCE, ")"), - new Operation.Binary("(", IntType.INSTANCE, " * ", IntType.INSTANCE, ")"), - new Operation.Binary("(", IntType.INSTANCE, " / ", IntType.INSTANCE, ")"), - new Operation.Binary("(", IntType.INSTANCE, " % ", IntType.INSTANCE, ")"), - new Operation.Binary("(", IntType.INSTANCE, " & ", IntType.INSTANCE, ")"), - new Operation.Binary("(", IntType.INSTANCE, " | ", IntType.INSTANCE, ")"), - new Operation.Binary("(", IntType.INSTANCE, " ^ ", IntType.INSTANCE, ")"), - new Operation.Binary("(", IntType.INSTANCE, " << ", IntType.INSTANCE, ")"), - new Operation.Binary("(", IntType.INSTANCE, " >> ", IntType.INSTANCE, ")"), - new Operation.Binary("(", IntType.INSTANCE, " >>> ", IntType.INSTANCE, ")"), - - new Operation.Binary("Byte.compare(", ByteType.INSTANCE, ", ", ByteType.INSTANCE, ")"), - new Operation.Binary("Byte.compareUnsigned(", ByteType.INSTANCE, ", ", ByteType.INSTANCE, ")"), - new Operation.Unary("Byte.toUnsignedInt(", ByteType.INSTANCE, ")"), - - new Operation.Binary("Character.compare(", CharType.INSTANCE, ", ", CharType.INSTANCE, ")"), - - new Operation.Binary("Short.compare(", ShortType.INSTANCE, ", ", ShortType.INSTANCE, ")"), - new Operation.Binary("Short.compareUnsigned(", ShortType.INSTANCE, ", ", ShortType.INSTANCE, ")"), - new Operation.Unary("Short.toUnsignedInt(", ShortType.INSTANCE, ")"), - - new Operation.Unary("Integer.bitCount(", IntType.INSTANCE, ")"), - new Operation.Binary("Integer.compare(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Binary("Integer.compareUnsigned(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Binary("Integer.compress(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Binary("Integer.divideUnsigned(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Binary("Integer.expand(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Unary("Integer.highestOneBit(", IntType.INSTANCE, ")"), - new Operation.Unary("Integer.lowestOneBit(", IntType.INSTANCE, ")"), - new Operation.Binary("Integer.min(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Binary("Integer.max(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Unary("Integer.numberOfLeadingZeros(", IntType.INSTANCE, ")"), - new Operation.Unary("Integer.numberOfTrailingZeros(", IntType.INSTANCE, ")"), - new Operation.Binary("Integer.remainderUnsigned(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Unary("Integer.reverse(", IntType.INSTANCE, ")"), - new Operation.Unary("Integer.reverseBytes(", IntType.INSTANCE, ")"), - new Operation.Binary("Integer.rotateLeft(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Binary("Integer.rotateRight(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Unary("Integer.signum(", IntType.INSTANCE, ")"), - new Operation.Binary("Integer.sum(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - - new Operation.Unary("Long.bitCount(", LongType.INSTANCE, ")"), - new Operation.Binary("Long.compare(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - new Operation.Binary("Long.compareUnsigned(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - new Operation.Unary("Long.numberOfLeadingZeros(", LongType.INSTANCE, ")"), - new Operation.Unary("Long.numberOfTrailingZeros(", LongType.INSTANCE, ")"), - new Operation.Unary("Long.signum(", LongType.INSTANCE, ")"), - - new Operation.Binary("Float.compare(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), - new Operation.Unary("Float.floatToIntBits(", FloatType.INSTANCE, ")"), - // Note: Float.floatToRawIntBits can lead to issues, because the NaN values are not always - // represented by the same int bits. - - new Operation.Binary("Double.compare(", DoubleType.INSTANCE, ", ", DoubleType.INSTANCE, ")"), - - new Operation.Binary("Boolean.compare(", BooleanType.INSTANCE, ", ", BooleanType.INSTANCE, ")"), - - new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", IntType.INSTANCE, " : ", IntType.INSTANCE, ")") - ); - @Override public final String name() { return "int"; } @@ -114,9 +37,4 @@ public final class IntType extends PrimitiveType { public final Object con() { return GEN_INT.next(); } - - @Override - public final List operations() { - return OPERATIONS; - } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java index fd4495d7f7880..ae9befc7e5ae1 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java @@ -23,68 +23,13 @@ package compiler.lib.template_library.types; -import java.util.List; - import compiler.lib.generators.Generators; import compiler.lib.generators.RestrictableGenerator; -import compiler.lib.template_library.Operation; - public final class LongType extends PrimitiveType { public static final LongType INSTANCE = new LongType(); private static final RestrictableGenerator GEN_LONG = Generators.G.longs(); - private static final List OPERATIONS = List.of( - new Operation.Unary("((long)", ByteType.INSTANCE, ")"), - new Operation.Unary("((long)", CharType.INSTANCE, ")"), - new Operation.Unary("((long)", ShortType.INSTANCE, ")"), - new Operation.Unary("((long)", IntType.INSTANCE, ")"), - new Operation.Unary("((long)", FloatType.INSTANCE, ")"), - new Operation.Unary("((long)", DoubleType.INSTANCE, ")"), - // Note: There is no cast from boolean - - new Operation.Unary("(-(", LongType.INSTANCE, "))"), - new Operation.Unary("(~", LongType.INSTANCE, ")"), - - new Operation.Binary("(", LongType.INSTANCE, " + ", LongType.INSTANCE, ")"), - new Operation.Binary("(", LongType.INSTANCE, " - ", LongType.INSTANCE, ")"), - new Operation.Binary("(", LongType.INSTANCE, " * ", LongType.INSTANCE, ")"), - new Operation.Binary("(", LongType.INSTANCE, " / ", LongType.INSTANCE, ")"), - new Operation.Binary("(", LongType.INSTANCE, " % ", LongType.INSTANCE, ")"), - new Operation.Binary("(", LongType.INSTANCE, " & ", LongType.INSTANCE, ")"), - new Operation.Binary("(", LongType.INSTANCE, " | ", LongType.INSTANCE, ")"), - new Operation.Binary("(", LongType.INSTANCE, " ^ ", LongType.INSTANCE, ")"), - new Operation.Binary("(", LongType.INSTANCE, " << ", LongType.INSTANCE, ")"), - new Operation.Binary("(", LongType.INSTANCE, " >> ", LongType.INSTANCE, ")"), - new Operation.Binary("(", LongType.INSTANCE, " >>> ", LongType.INSTANCE, ")"), - - new Operation.Unary("Byte.toUnsignedLong(", ByteType.INSTANCE, ")"), - - new Operation.Unary("Short.toUnsignedLong(", ShortType.INSTANCE, ")"), - - new Operation.Unary("Integer.toUnsignedLong(", IntType.INSTANCE, ")"), - - new Operation.Binary("Long.compress(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - new Operation.Binary("Long.divideUnsigned(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - new Operation.Binary("Long.expand(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - new Operation.Unary("Long.highestOneBit(", LongType.INSTANCE, ")"), - new Operation.Unary("Long.lowestOneBit(", LongType.INSTANCE, ")"), - new Operation.Binary("Long.min(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - new Operation.Binary("Long.max(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - new Operation.Binary("Long.remainderUnsigned(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - new Operation.Unary("Long.reverse(", LongType.INSTANCE, ")"), - new Operation.Unary("Long.reverseBytes(", LongType.INSTANCE, ")"), - new Operation.Binary("Long.rotateLeft(", LongType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Binary("Long.rotateRight(", LongType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Binary("Long.sum(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - - new Operation.Unary("Double.doubleToLongBits(", DoubleType.INSTANCE, ")"), - // Note: Double.doubleToRawLongBits can lead to issues, because the NaN values are not always - // represented by the same long bits. - - new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", LongType.INSTANCE, " : ", LongType.INSTANCE, ")") - ); - @Override public final String name() { return "long"; } @@ -92,9 +37,4 @@ public final class LongType extends PrimitiveType { public final Object con() { return GEN_LONG.next(); } - - @Override - public final List operations() { - return OPERATIONS; - } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java index 368eb3b509959..d538def3eff8e 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java @@ -23,42 +23,13 @@ package compiler.lib.template_library.types; -import java.util.List; - import compiler.lib.generators.Generators; import compiler.lib.generators.RestrictableGenerator; -import compiler.lib.template_library.Operation; - public final class ShortType extends PrimitiveType { public static final ShortType INSTANCE = new ShortType(); private static final RestrictableGenerator GEN_SHORT = Generators.G.safeRestrict(Generators.G.ints(), Short.MIN_VALUE, Short.MAX_VALUE); - private static final List OPERATIONS = List.of( - // Note: the standard integer arithmetic operations are only defined for int/long. - // They can be used for smaller types only via automatic promotion to int, - // and then a cast back to short, e.g: - // short a = (short)(b + c) - // - // Instead of adding these operations explicitly, we just add the conversion - // from int to short, and let the IntType generate all the integer arithmetic - // operations. - new Operation.Unary("((short)", ByteType.INSTANCE, ")"), - new Operation.Unary("((short)", CharType.INSTANCE, ")"), - new Operation.Unary("((short)", IntType.INSTANCE, ")"), - new Operation.Unary("((short)", LongType.INSTANCE, ")"), - new Operation.Unary("((short)", FloatType.INSTANCE, ")"), - new Operation.Unary("((short)", DoubleType.INSTANCE, ")"), - // Note: There is no cast from boolean - - new Operation.Unary("Short.reverseBytes(", ShortType.INSTANCE, ")"), - - // Note: Float.floatToFloat16 can lead to issues, because NaN values are not always - // represented by the same short bits. - - new Operation.Ternary("(", BooleanType.INSTANCE, " ? ", ShortType.INSTANCE, " : ", ShortType.INSTANCE, ")") - ); - @Override public final String name() { return "short"; } @@ -66,9 +37,4 @@ public final class ShortType extends PrimitiveType { public final Object con() { return "(short)" + GEN_SHORT.next(); } - - @Override - public final List operations() { - return OPERATIONS; - } } From 330b96673c95edbd0c04c4e118c9a71a402eb5fb Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 27 Feb 2025 19:42:12 +0100 Subject: [PATCH 127/212] type refactor --- .../lib/template_library/Operations.java | 373 +++++++++--------- .../lib/template_library/PrimitiveType.java | 147 +++++++ .../compiler/lib/template_library/Type.java | 34 +- .../template_library/types/BooleanType.java | 41 -- .../lib/template_library/types/ByteType.java | 40 -- .../lib/template_library/types/CharType.java | 40 -- .../template_library/types/DoubleType.java | 40 -- .../lib/template_library/types/FloatType.java | 40 -- .../lib/template_library/types/IntType.java | 40 -- .../lib/template_library/types/LongType.java | 40 -- .../template_library/types/PrimitiveType.java | 36 -- .../lib/template_library/types/ShortType.java | 40 -- 12 files changed, 346 insertions(+), 565 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/PrimitiveType.java delete mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java delete mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/ByteType.java delete mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/CharType.java delete mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java delete mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java delete mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java delete mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java delete mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/PrimitiveType.java delete mode 100644 test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 6eb915149cb48..9404596f797c4 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -26,15 +26,6 @@ import java.util.List; import java.util.stream.Stream; -import compiler.lib.template_library.types.ByteType; -import compiler.lib.template_library.types.CharType; -import compiler.lib.template_library.types.ShortType; -import compiler.lib.template_library.types.IntType; -import compiler.lib.template_library.types.LongType; -import compiler.lib.template_library.types.FloatType; -import compiler.lib.template_library.types.DoubleType; -import compiler.lib.template_library.types.BooleanType; - public class Operations { private static final List BYTE_OPERATIONS = List.of( @@ -46,15 +37,15 @@ public class Operations { // Instead of adding these operations explicitly, we just add the conversion // from int to byte, and let the IntType generate all the integer arithmetic // operations. - new Operation.Unary(ByteType.INSTANCE, "((byte)", CharType.INSTANCE, ")"), - new Operation.Unary(ByteType.INSTANCE, "((byte)", ShortType.INSTANCE, ")"), - new Operation.Unary(ByteType.INSTANCE, "((byte)", IntType.INSTANCE, ")"), - new Operation.Unary(ByteType.INSTANCE, "((byte)", LongType.INSTANCE, ")"), - new Operation.Unary(ByteType.INSTANCE, "((byte)", FloatType.INSTANCE, ")"), - new Operation.Unary(ByteType.INSTANCE, "((byte)", DoubleType.INSTANCE, ")"), + new Operation.Unary(Type.bytes(), "((byte)", Type.chars(), ")"), + new Operation.Unary(Type.bytes(), "((byte)", Type.shorts(), ")"), + new Operation.Unary(Type.bytes(), "((byte)", Type.ints(), ")"), + new Operation.Unary(Type.bytes(), "((byte)", Type.longs(), ")"), + new Operation.Unary(Type.bytes(), "((byte)", Type.floats(), ")"), + new Operation.Unary(Type.bytes(), "((byte)", Type.doubles(), ")"), // Note: There is no cast from boolean - new Operation.Ternary(ByteType.INSTANCE, "(", BooleanType.INSTANCE, " ? ", ByteType.INSTANCE, " : ", ByteType.INSTANCE, ")") + new Operation.Ternary(Type.bytes(), "(", Type.booleans(), " ? ", Type.bytes(), " : ", Type.bytes(), ")") ); private static final List CHAR_OPERATIONS = List.of( @@ -66,17 +57,17 @@ public class Operations { // Instead of adding these operations explicitly, we just add the conversion // from int to char, and let the IntType generate all the integer arithmetic // operations. - new Operation.Unary(CharType.INSTANCE, "((char)", ByteType.INSTANCE, ")"), - new Operation.Unary(CharType.INSTANCE, "((char)", ShortType.INSTANCE, ")"), - new Operation.Unary(CharType.INSTANCE, "((char)", IntType.INSTANCE, ")"), - new Operation.Unary(CharType.INSTANCE, "((char)", LongType.INSTANCE, ")"), - new Operation.Unary(CharType.INSTANCE, "((char)", FloatType.INSTANCE, ")"), - new Operation.Unary(CharType.INSTANCE, "((char)", DoubleType.INSTANCE, ")"), + new Operation.Unary(Type.chars(), "((char)", Type.bytes(), ")"), + new Operation.Unary(Type.chars(), "((char)", Type.shorts(), ")"), + new Operation.Unary(Type.chars(), "((char)", Type.ints(), ")"), + new Operation.Unary(Type.chars(), "((char)", Type.longs(), ")"), + new Operation.Unary(Type.chars(), "((char)", Type.floats(), ")"), + new Operation.Unary(Type.chars(), "((char)", Type.doubles(), ")"), // Note: There is no cast from boolean - new Operation.Unary(CharType.INSTANCE, "Character.reverseBytes(", CharType.INSTANCE, ")"), + new Operation.Unary(Type.chars(), "Character.reverseBytes(", Type.chars(), ")"), - new Operation.Ternary(CharType.INSTANCE, "(", BooleanType.INSTANCE, " ? ", CharType.INSTANCE, " : ", CharType.INSTANCE, ")") + new Operation.Ternary(Type.chars(), "(", Type.booleans(), " ? ", Type.chars(), " : ", Type.chars(), ")") ); private static final List SHORT_OPERATIONS = List.of( @@ -88,220 +79,220 @@ public class Operations { // Instead of adding these operations explicitly, we just add the conversion // from int to short, and let the IntType generate all the integer arithmetic // operations. - new Operation.Unary(ShortType.INSTANCE, "((short)", ByteType.INSTANCE, ")"), - new Operation.Unary(ShortType.INSTANCE, "((short)", CharType.INSTANCE, ")"), - new Operation.Unary(ShortType.INSTANCE, "((short)", IntType.INSTANCE, ")"), - new Operation.Unary(ShortType.INSTANCE, "((short)", LongType.INSTANCE, ")"), - new Operation.Unary(ShortType.INSTANCE, "((short)", FloatType.INSTANCE, ")"), - new Operation.Unary(ShortType.INSTANCE, "((short)", DoubleType.INSTANCE, ")"), + new Operation.Unary(Type.shorts(), "((short)", Type.bytes(), ")"), + new Operation.Unary(Type.shorts(), "((short)", Type.chars(), ")"), + new Operation.Unary(Type.shorts(), "((short)", Type.ints(), ")"), + new Operation.Unary(Type.shorts(), "((short)", Type.longs(), ")"), + new Operation.Unary(Type.shorts(), "((short)", Type.floats(), ")"), + new Operation.Unary(Type.shorts(), "((short)", Type.doubles(), ")"), // Note: There is no cast from boolean - new Operation.Unary(ShortType.INSTANCE, "Short.reverseBytes(", ShortType.INSTANCE, ")"), + new Operation.Unary(Type.shorts(), "Short.reverseBytes(", Type.shorts(), ")"), // Note: Float.floatToFloat16 can lead to issues, because NaN values are not always // represented by the same short bits. - new Operation.Ternary(ShortType.INSTANCE, "(", BooleanType.INSTANCE, " ? ", ShortType.INSTANCE, " : ", ShortType.INSTANCE, ")") + new Operation.Ternary(Type.shorts(), "(", Type.booleans(), " ? ", Type.shorts(), " : ", Type.shorts(), ")") ); private static final List INT_OPERATIONS = List.of( - new Operation.Unary(IntType.INSTANCE, "((int)", ByteType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "((int)", CharType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "((int)", ShortType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "((int)", LongType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "((int)", FloatType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "((int)", DoubleType.INSTANCE, ")"), + new Operation.Unary(Type.ints(), "((int)", Type.bytes(), ")"), + new Operation.Unary(Type.ints(), "((int)", Type.chars(), ")"), + new Operation.Unary(Type.ints(), "((int)", Type.shorts(), ")"), + new Operation.Unary(Type.ints(), "((int)", Type.longs(), ")"), + new Operation.Unary(Type.ints(), "((int)", Type.floats(), ")"), + new Operation.Unary(Type.ints(), "((int)", Type.doubles(), ")"), // Note: There is no cast from boolean - new Operation.Unary(IntType.INSTANCE, "(-(", IntType.INSTANCE, "))"), - new Operation.Unary(IntType.INSTANCE, "(~", IntType.INSTANCE, ")"), - - new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " + ", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " - ", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " * ", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " / ", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " % ", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " & ", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " | ", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " ^ ", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " << ", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " >> ", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "(", IntType.INSTANCE, " >>> ", IntType.INSTANCE, ")"), - - new Operation.Binary(IntType.INSTANCE, "Byte.compare(", ByteType.INSTANCE, ", ", ByteType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "Byte.compareUnsigned(", ByteType.INSTANCE, ", ", ByteType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "Byte.toUnsignedInt(", ByteType.INSTANCE, ")"), - - new Operation.Binary(IntType.INSTANCE, "Character.compare(", CharType.INSTANCE, ", ", CharType.INSTANCE, ")"), - - new Operation.Binary(IntType.INSTANCE, "Short.compare(", ShortType.INSTANCE, ", ", ShortType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "Short.compareUnsigned(", ShortType.INSTANCE, ", ", ShortType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "Short.toUnsignedInt(", ShortType.INSTANCE, ")"), - - new Operation.Unary(IntType.INSTANCE, "Integer.bitCount(", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "Integer.compare(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "Integer.compareUnsigned(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "Integer.compress(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "Integer.divideUnsigned(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "Integer.expand(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "Integer.highestOneBit(", IntType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "Integer.lowestOneBit(", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "Integer.min(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "Integer.max(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "Integer.numberOfLeadingZeros(", IntType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "Integer.numberOfTrailingZeros(", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "Integer.remainderUnsigned(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "Integer.reverse(", IntType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "Integer.reverseBytes(", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "Integer.rotateLeft(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "Integer.rotateRight(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "Integer.signum(", IntType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "Integer.sum(", IntType.INSTANCE, ", ", IntType.INSTANCE, ")"), - - new Operation.Unary(IntType.INSTANCE, "Long.bitCount(", LongType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "Long.compare(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - new Operation.Binary(IntType.INSTANCE, "Long.compareUnsigned(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "Long.numberOfLeadingZeros(", LongType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "Long.numberOfTrailingZeros(", LongType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "Long.signum(", LongType.INSTANCE, ")"), - - new Operation.Binary(IntType.INSTANCE, "Float.compare(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), - new Operation.Unary(IntType.INSTANCE, "Float.floatToIntBits(", FloatType.INSTANCE, ")"), + new Operation.Unary(Type.ints(), "(-(", Type.ints(), "))"), + new Operation.Unary(Type.ints(), "(~", Type.ints(), ")"), + + new Operation.Binary(Type.ints(), "(", Type.ints(), " + ", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "(", Type.ints(), " - ", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "(", Type.ints(), " * ", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "(", Type.ints(), " / ", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "(", Type.ints(), " % ", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "(", Type.ints(), " & ", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "(", Type.ints(), " | ", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "(", Type.ints(), " ^ ", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "(", Type.ints(), " << ", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "(", Type.ints(), " >> ", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "(", Type.ints(), " >>> ", Type.ints(), ")"), + + new Operation.Binary(Type.ints(), "Byte.compare(", Type.bytes(), ", ", Type.bytes(), ")"), + new Operation.Binary(Type.ints(), "Byte.compareUnsigned(", Type.bytes(), ", ", Type.bytes(), ")"), + new Operation.Unary(Type.ints(), "Byte.toUnsignedInt(", Type.bytes(), ")"), + + new Operation.Binary(Type.ints(), "Character.compare(", Type.chars(), ", ", Type.chars(), ")"), + + new Operation.Binary(Type.ints(), "Short.compare(", Type.shorts(), ", ", Type.shorts(), ")"), + new Operation.Binary(Type.ints(), "Short.compareUnsigned(", Type.shorts(), ", ", Type.shorts(), ")"), + new Operation.Unary(Type.ints(), "Short.toUnsignedInt(", Type.shorts(), ")"), + + new Operation.Unary(Type.ints(), "Integer.bitCount(", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "Integer.compare(", Type.ints(), ", ", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "Integer.compareUnsigned(", Type.ints(), ", ", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "Integer.compress(", Type.ints(), ", ", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "Integer.divideUnsigned(", Type.ints(), ", ", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "Integer.expand(", Type.ints(), ", ", Type.ints(), ")"), + new Operation.Unary(Type.ints(), "Integer.highestOneBit(", Type.ints(), ")"), + new Operation.Unary(Type.ints(), "Integer.lowestOneBit(", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "Integer.min(", Type.ints(), ", ", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "Integer.max(", Type.ints(), ", ", Type.ints(), ")"), + new Operation.Unary(Type.ints(), "Integer.numberOfLeadingZeros(", Type.ints(), ")"), + new Operation.Unary(Type.ints(), "Integer.numberOfTrailingZeros(", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "Integer.remainderUnsigned(", Type.ints(), ", ", Type.ints(), ")"), + new Operation.Unary(Type.ints(), "Integer.reverse(", Type.ints(), ")"), + new Operation.Unary(Type.ints(), "Integer.reverseBytes(", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "Integer.rotateLeft(", Type.ints(), ", ", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "Integer.rotateRight(", Type.ints(), ", ", Type.ints(), ")"), + new Operation.Unary(Type.ints(), "Integer.signum(", Type.ints(), ")"), + new Operation.Binary(Type.ints(), "Integer.sum(", Type.ints(), ", ", Type.ints(), ")"), + + new Operation.Unary(Type.ints(), "Long.bitCount(", Type.longs(), ")"), + new Operation.Binary(Type.ints(), "Long.compare(", Type.longs(), ", ", Type.longs(), ")"), + new Operation.Binary(Type.ints(), "Long.compareUnsigned(", Type.longs(), ", ", Type.longs(), ")"), + new Operation.Unary(Type.ints(), "Long.numberOfLeadingZeros(", Type.longs(), ")"), + new Operation.Unary(Type.ints(), "Long.numberOfTrailingZeros(", Type.longs(), ")"), + new Operation.Unary(Type.ints(), "Long.signum(", Type.longs(), ")"), + + new Operation.Binary(Type.ints(), "Float.compare(", Type.floats(), ", ", Type.floats(), ")"), + new Operation.Unary(Type.ints(), "Float.floatToIntBits(", Type.floats(), ")"), // Note: Float.floatToRawIntBits can lead to issues, because the NaN values are not always // represented by the same int bits. - new Operation.Binary(IntType.INSTANCE, "Double.compare(", DoubleType.INSTANCE, ", ", DoubleType.INSTANCE, ")"), + new Operation.Binary(Type.ints(), "Double.compare(", Type.doubles(), ", ", Type.doubles(), ")"), - new Operation.Binary(IntType.INSTANCE, "Boolean.compare(", BooleanType.INSTANCE, ", ", BooleanType.INSTANCE, ")"), + new Operation.Binary(Type.ints(), "Boolean.compare(", Type.booleans(), ", ", Type.booleans(), ")"), - new Operation.Ternary(IntType.INSTANCE, "(", BooleanType.INSTANCE, " ? ", IntType.INSTANCE, " : ", IntType.INSTANCE, ")") + new Operation.Ternary(Type.ints(), "(", Type.booleans(), " ? ", Type.ints(), " : ", Type.ints(), ")") ); private static final List LONG_OPERATIONS = List.of( - new Operation.Unary(LongType.INSTANCE, "((long)", ByteType.INSTANCE, ")"), - new Operation.Unary(LongType.INSTANCE, "((long)", CharType.INSTANCE, ")"), - new Operation.Unary(LongType.INSTANCE, "((long)", ShortType.INSTANCE, ")"), - new Operation.Unary(LongType.INSTANCE, "((long)", IntType.INSTANCE, ")"), - new Operation.Unary(LongType.INSTANCE, "((long)", FloatType.INSTANCE, ")"), - new Operation.Unary(LongType.INSTANCE, "((long)", DoubleType.INSTANCE, ")"), + new Operation.Unary(Type.longs(), "((long)", Type.bytes(), ")"), + new Operation.Unary(Type.longs(), "((long)", Type.chars(), ")"), + new Operation.Unary(Type.longs(), "((long)", Type.shorts(), ")"), + new Operation.Unary(Type.longs(), "((long)", Type.ints(), ")"), + new Operation.Unary(Type.longs(), "((long)", Type.floats(), ")"), + new Operation.Unary(Type.longs(), "((long)", Type.doubles(), ")"), // Note: There is no cast from boolean - new Operation.Unary(LongType.INSTANCE, "(-(", LongType.INSTANCE, "))"), - new Operation.Unary(LongType.INSTANCE, "(~", LongType.INSTANCE, ")"), - - new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " + ", LongType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " - ", LongType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " * ", LongType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " / ", LongType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " % ", LongType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " & ", LongType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " | ", LongType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " ^ ", LongType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " << ", LongType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " >> ", LongType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "(", LongType.INSTANCE, " >>> ", LongType.INSTANCE, ")"), - - new Operation.Unary(LongType.INSTANCE, "Byte.toUnsignedLong(", ByteType.INSTANCE, ")"), - - new Operation.Unary(LongType.INSTANCE, "Short.toUnsignedLong(", ShortType.INSTANCE, ")"), - - new Operation.Unary(LongType.INSTANCE, "Integer.toUnsignedLong(", IntType.INSTANCE, ")"), - - new Operation.Binary(LongType.INSTANCE, "Long.compress(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "Long.divideUnsigned(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "Long.expand(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - new Operation.Unary(LongType.INSTANCE, "Long.highestOneBit(", LongType.INSTANCE, ")"), - new Operation.Unary(LongType.INSTANCE, "Long.lowestOneBit(", LongType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "Long.min(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "Long.max(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "Long.remainderUnsigned(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - new Operation.Unary(LongType.INSTANCE, "Long.reverse(", LongType.INSTANCE, ")"), - new Operation.Unary(LongType.INSTANCE, "Long.reverseBytes(", LongType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "Long.rotateLeft(", LongType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "Long.rotateRight(", LongType.INSTANCE, ", ", IntType.INSTANCE, ")"), - new Operation.Binary(LongType.INSTANCE, "Long.sum(", LongType.INSTANCE, ", ", LongType.INSTANCE, ")"), - - new Operation.Unary(LongType.INSTANCE, "Double.doubleToLongBits(", DoubleType.INSTANCE, ")"), + new Operation.Unary(Type.longs(), "(-(", Type.longs(), "))"), + new Operation.Unary(Type.longs(), "(~", Type.longs(), ")"), + + new Operation.Binary(Type.longs(), "(", Type.longs(), " + ", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "(", Type.longs(), " - ", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "(", Type.longs(), " * ", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "(", Type.longs(), " / ", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "(", Type.longs(), " % ", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "(", Type.longs(), " & ", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "(", Type.longs(), " | ", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "(", Type.longs(), " ^ ", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "(", Type.longs(), " << ", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "(", Type.longs(), " >> ", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "(", Type.longs(), " >>> ", Type.longs(), ")"), + + new Operation.Unary(Type.longs(), "Byte.toUnsignedLong(", Type.bytes(), ")"), + + new Operation.Unary(Type.longs(), "Short.toUnsignedLong(", Type.shorts(), ")"), + + new Operation.Unary(Type.longs(), "Integer.toUnsignedLong(", Type.ints(), ")"), + + new Operation.Binary(Type.longs(), "Long.compress(", Type.longs(), ", ", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "Long.divideUnsigned(", Type.longs(), ", ", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "Long.expand(", Type.longs(), ", ", Type.longs(), ")"), + new Operation.Unary(Type.longs(), "Long.highestOneBit(", Type.longs(), ")"), + new Operation.Unary(Type.longs(), "Long.lowestOneBit(", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "Long.min(", Type.longs(), ", ", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "Long.max(", Type.longs(), ", ", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "Long.remainderUnsigned(", Type.longs(), ", ", Type.longs(), ")"), + new Operation.Unary(Type.longs(), "Long.reverse(", Type.longs(), ")"), + new Operation.Unary(Type.longs(), "Long.reverseBytes(", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "Long.rotateLeft(", Type.longs(), ", ", Type.ints(), ")"), + new Operation.Binary(Type.longs(), "Long.rotateRight(", Type.longs(), ", ", Type.ints(), ")"), + new Operation.Binary(Type.longs(), "Long.sum(", Type.longs(), ", ", Type.longs(), ")"), + + new Operation.Unary(Type.longs(), "Double.doubleToLongBits(", Type.doubles(), ")"), // Note: Double.doubleToRawLongBits can lead to issues, because the NaN values are not always // represented by the same long bits. - new Operation.Ternary(LongType.INSTANCE, "(", BooleanType.INSTANCE, " ? ", LongType.INSTANCE, " : ", LongType.INSTANCE, ")") + new Operation.Ternary(Type.longs(), "(", Type.booleans(), " ? ", Type.longs(), " : ", Type.longs(), ")") ); private static final List FLOAT_OPERATIONS = List.of( - new Operation.Unary(FloatType.INSTANCE, "((float)", ByteType.INSTANCE, ")"), - new Operation.Unary(FloatType.INSTANCE, "((float)", CharType.INSTANCE, ")"), - new Operation.Unary(FloatType.INSTANCE, "((float)", ShortType.INSTANCE, ")"), - new Operation.Unary(FloatType.INSTANCE, "((float)", IntType.INSTANCE, ")"), - new Operation.Unary(FloatType.INSTANCE, "((float)", LongType.INSTANCE, ")"), - new Operation.Unary(FloatType.INSTANCE, "((float)", DoubleType.INSTANCE, ")"), - - new Operation.Unary(FloatType.INSTANCE, "(-(", FloatType.INSTANCE, "))"), - - new Operation.Binary(FloatType.INSTANCE, "(", FloatType.INSTANCE, " + ", FloatType.INSTANCE, ")"), - new Operation.Binary(FloatType.INSTANCE, "(", FloatType.INSTANCE, " - ", FloatType.INSTANCE, ")"), - new Operation.Binary(FloatType.INSTANCE, "(", FloatType.INSTANCE, " * ", FloatType.INSTANCE, ")"), - new Operation.Binary(FloatType.INSTANCE, "(", FloatType.INSTANCE, " / ", FloatType.INSTANCE, ")"), - new Operation.Binary(FloatType.INSTANCE, "(", FloatType.INSTANCE, " % ", FloatType.INSTANCE, ")"), - - new Operation.Unary(FloatType.INSTANCE, "Float.float16ToFloat(", ShortType.INSTANCE, ")"), - new Operation.Unary(FloatType.INSTANCE, "Float.intBitsToFloat(", IntType.INSTANCE, ")"), - new Operation.Binary(FloatType.INSTANCE, "Float.max(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), - new Operation.Binary(FloatType.INSTANCE, "Float.min(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), - new Operation.Binary(FloatType.INSTANCE, "Float.sum(", FloatType.INSTANCE, ", ", FloatType.INSTANCE, ")"), - - new Operation.Ternary(FloatType.INSTANCE, "(", BooleanType.INSTANCE, " ? ", FloatType.INSTANCE, " : ", FloatType.INSTANCE, ")") + new Operation.Unary(Type.floats(), "((float)", Type.bytes(), ")"), + new Operation.Unary(Type.floats(), "((float)", Type.chars(), ")"), + new Operation.Unary(Type.floats(), "((float)", Type.shorts(), ")"), + new Operation.Unary(Type.floats(), "((float)", Type.ints(), ")"), + new Operation.Unary(Type.floats(), "((float)", Type.longs(), ")"), + new Operation.Unary(Type.floats(), "((float)", Type.doubles(), ")"), + + new Operation.Unary(Type.floats(), "(-(", Type.floats(), "))"), + + new Operation.Binary(Type.floats(), "(", Type.floats(), " + ", Type.floats(), ")"), + new Operation.Binary(Type.floats(), "(", Type.floats(), " - ", Type.floats(), ")"), + new Operation.Binary(Type.floats(), "(", Type.floats(), " * ", Type.floats(), ")"), + new Operation.Binary(Type.floats(), "(", Type.floats(), " / ", Type.floats(), ")"), + new Operation.Binary(Type.floats(), "(", Type.floats(), " % ", Type.floats(), ")"), + + new Operation.Unary(Type.floats(), "Float.float16ToFloat(", Type.shorts(), ")"), + new Operation.Unary(Type.floats(), "Float.intBitsToFloat(", Type.ints(), ")"), + new Operation.Binary(Type.floats(), "Float.max(", Type.floats(), ", ", Type.floats(), ")"), + new Operation.Binary(Type.floats(), "Float.min(", Type.floats(), ", ", Type.floats(), ")"), + new Operation.Binary(Type.floats(), "Float.sum(", Type.floats(), ", ", Type.floats(), ")"), + + new Operation.Ternary(Type.floats(), "(", Type.booleans(), " ? ", Type.floats(), " : ", Type.floats(), ")") ); private static final List DOUBLE_OPERATIONS = List.of( - new Operation.Unary(DoubleType.INSTANCE, "((double)", ByteType.INSTANCE, ")"), - new Operation.Unary(DoubleType.INSTANCE, "((double)", CharType.INSTANCE, ")"), - new Operation.Unary(DoubleType.INSTANCE, "((double)", ShortType.INSTANCE, ")"), - new Operation.Unary(DoubleType.INSTANCE, "((double)", IntType.INSTANCE, ")"), - new Operation.Unary(DoubleType.INSTANCE, "((double)", LongType.INSTANCE, ")"), - new Operation.Unary(DoubleType.INSTANCE, "((double)", FloatType.INSTANCE, ")"), + new Operation.Unary(Type.doubles(), "((double)", Type.bytes(), ")"), + new Operation.Unary(Type.doubles(), "((double)", Type.chars(), ")"), + new Operation.Unary(Type.doubles(), "((double)", Type.shorts(), ")"), + new Operation.Unary(Type.doubles(), "((double)", Type.ints(), ")"), + new Operation.Unary(Type.doubles(), "((double)", Type.longs(), ")"), + new Operation.Unary(Type.doubles(), "((double)", Type.floats(), ")"), // Note: There is no cast from boolean - new Operation.Unary(DoubleType.INSTANCE, "(-(", DoubleType.INSTANCE, "))"), + new Operation.Unary(Type.doubles(), "(-(", Type.doubles(), "))"), - new Operation.Binary(DoubleType.INSTANCE, "(", DoubleType.INSTANCE, " + ", DoubleType.INSTANCE, ")"), - new Operation.Binary(DoubleType.INSTANCE, "(", DoubleType.INSTANCE, " - ", DoubleType.INSTANCE, ")"), - new Operation.Binary(DoubleType.INSTANCE, "(", DoubleType.INSTANCE, " * ", DoubleType.INSTANCE, ")"), - new Operation.Binary(DoubleType.INSTANCE, "(", DoubleType.INSTANCE, " / ", DoubleType.INSTANCE, ")"), - new Operation.Binary(DoubleType.INSTANCE, "(", DoubleType.INSTANCE, " % ", DoubleType.INSTANCE, ")"), + new Operation.Binary(Type.doubles(), "(", Type.doubles(), " + ", Type.doubles(), ")"), + new Operation.Binary(Type.doubles(), "(", Type.doubles(), " - ", Type.doubles(), ")"), + new Operation.Binary(Type.doubles(), "(", Type.doubles(), " * ", Type.doubles(), ")"), + new Operation.Binary(Type.doubles(), "(", Type.doubles(), " / ", Type.doubles(), ")"), + new Operation.Binary(Type.doubles(), "(", Type.doubles(), " % ", Type.doubles(), ")"), - new Operation.Unary(DoubleType.INSTANCE, "Double.longBitsToDouble(", IntType.INSTANCE, ")"), - new Operation.Binary(DoubleType.INSTANCE, "Double.max(", DoubleType.INSTANCE, ", ", DoubleType.INSTANCE, ")"), - new Operation.Binary(DoubleType.INSTANCE, "Double.min(", DoubleType.INSTANCE, ", ", DoubleType.INSTANCE, ")"), - new Operation.Binary(DoubleType.INSTANCE, "Double.sum(", DoubleType.INSTANCE, ", ", DoubleType.INSTANCE, ")"), + new Operation.Unary(Type.doubles(), "Double.longBitsToDouble(", Type.ints(), ")"), + new Operation.Binary(Type.doubles(), "Double.max(", Type.doubles(), ", ", Type.doubles(), ")"), + new Operation.Binary(Type.doubles(), "Double.min(", Type.doubles(), ", ", Type.doubles(), ")"), + new Operation.Binary(Type.doubles(), "Double.sum(", Type.doubles(), ", ", Type.doubles(), ")"), - new Operation.Ternary(DoubleType.INSTANCE, "(", BooleanType.INSTANCE, " ? ", DoubleType.INSTANCE, " : ", DoubleType.INSTANCE, ")") + new Operation.Ternary(Type.doubles(), "(", Type.booleans(), " ? ", Type.doubles(), " : ", Type.doubles(), ")") ); private static final List BOOLEAN_OPERATIONS = List.of( // Note: there is no casting / conversion from an to boolean directly. - new Operation.Unary(BooleanType.INSTANCE, "(!(", BooleanType.INSTANCE, "))"), + new Operation.Unary(Type.booleans(), "(!(", Type.booleans(), "))"), - new Operation.Binary(BooleanType.INSTANCE, "(", BooleanType.INSTANCE, " || ", BooleanType.INSTANCE, ")"), - new Operation.Binary(BooleanType.INSTANCE, "(", BooleanType.INSTANCE, " && ", BooleanType.INSTANCE, ")"), - new Operation.Binary(BooleanType.INSTANCE, "(", BooleanType.INSTANCE, " ^ ", BooleanType.INSTANCE, ")"), + new Operation.Binary(Type.booleans(), "(", Type.booleans(), " || ", Type.booleans(), ")"), + new Operation.Binary(Type.booleans(), "(", Type.booleans(), " && ", Type.booleans(), ")"), + new Operation.Binary(Type.booleans(), "(", Type.booleans(), " ^ ", Type.booleans(), ")"), - new Operation.Binary(BooleanType.INSTANCE, "Boolean.logicalAnd(", BooleanType.INSTANCE, ", ", BooleanType.INSTANCE, ")"), - new Operation.Binary(BooleanType.INSTANCE, "Boolean.logicalOr(", BooleanType.INSTANCE, ", ", BooleanType.INSTANCE, ")"), - new Operation.Binary(BooleanType.INSTANCE, "Boolean.logicalXor(", BooleanType.INSTANCE, ", ", BooleanType.INSTANCE, ")"), + new Operation.Binary(Type.booleans(), "Boolean.logicalAnd(", Type.booleans(), ", ", Type.booleans(), ")"), + new Operation.Binary(Type.booleans(), "Boolean.logicalOr(", Type.booleans(), ", ", Type.booleans(), ")"), + new Operation.Binary(Type.booleans(), "Boolean.logicalXor(", Type.booleans(), ", ", Type.booleans(), ")"), // Note: For now, we are omitting all the Character.is<...> methods. We can add them in the future. - new Operation.Unary(BooleanType.INSTANCE, "Float.isFinite(", FloatType.INSTANCE, ")"), - new Operation.Unary(BooleanType.INSTANCE, "Float.isInfinite(", FloatType.INSTANCE, ")"), - new Operation.Unary(BooleanType.INSTANCE, "Float.isNaN(", FloatType.INSTANCE, ")"), + new Operation.Unary(Type.booleans(), "Float.isFinite(", Type.floats(), ")"), + new Operation.Unary(Type.booleans(), "Float.isInfinite(", Type.floats(), ")"), + new Operation.Unary(Type.booleans(), "Float.isNaN(", Type.floats(), ")"), - new Operation.Unary(BooleanType.INSTANCE, "Double.isFinite(", DoubleType.INSTANCE, ")"), - new Operation.Unary(BooleanType.INSTANCE, "Double.isInfinite(", DoubleType.INSTANCE, ")"), - new Operation.Unary(BooleanType.INSTANCE, "Double.isNaN(", DoubleType.INSTANCE, ")"), + new Operation.Unary(Type.booleans(), "Double.isFinite(", Type.doubles(), ")"), + new Operation.Unary(Type.booleans(), "Double.isInfinite(", Type.doubles(), ")"), + new Operation.Unary(Type.booleans(), "Double.isNaN(", Type.doubles(), ")"), - new Operation.Ternary(BooleanType.INSTANCE, "(", BooleanType.INSTANCE, " ? ", BooleanType.INSTANCE, " : ", BooleanType.INSTANCE, ")") + new Operation.Ternary(Type.booleans(), "(", Type.booleans(), " ? ", Type.booleans(), " : ", Type.booleans(), ")") ); public static final List PRIMITIVE_OPERATIONS = Stream.of( diff --git a/test/hotspot/jtreg/compiler/lib/template_library/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_library/PrimitiveType.java new file mode 100644 index 0000000000000..7899b08690766 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/PrimitiveType.java @@ -0,0 +1,147 @@ +/* + * 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_library; + +import java.util.Random; +import jdk.test.lib.Utils; + +import compiler.lib.generators.Generators; +import compiler.lib.generators.Generator; +import compiler.lib.generators.RestrictableGenerator; + +import compiler.lib.template_framework.Name; + +abstract class PrimitiveType extends Type { + 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(); + + @Override + public boolean isSubtypeOf(Name.Type other) { + // Primitives are only subtypes of their own Primitive type. + return this.getClass() == other.getClass(); + } + + static final class ByteType extends PrimitiveType { + public static final ByteType INSTANCE = new ByteType(); + + @Override + public final String name() { return "byte"; } + + @Override + public final Object con() { + return "(byte)" + GEN_BYTE.next(); + } + } + + static final class CharType extends PrimitiveType { + public static final CharType INSTANCE = new CharType(); + + @Override + public final String name() { return "char"; } + + @Override + public final Object con() { + return "(char)" + GEN_CHAR.next(); + } + } + + static final class ShortType extends PrimitiveType { + public static final ShortType INSTANCE = new ShortType(); + + @Override + public final String name() { return "short"; } + + @Override + public final Object con() { + return "(short)" + GEN_SHORT.next(); + } + } + + static final class IntType extends PrimitiveType { + public static final IntType INSTANCE = new IntType(); + + @Override + public final String name() { return "int"; } + + @Override + public final Object con() { + return GEN_INT.next(); + } + } + + static final class LongType extends PrimitiveType { + public static final LongType INSTANCE = new LongType(); + + @Override + public final String name() { return "long"; } + + @Override + public final Object con() { + return GEN_LONG.next(); + } + } + + static final class FloatType extends PrimitiveType { + public static final FloatType INSTANCE = new FloatType(); + + @Override + public final String name() { return "float"; } + + @Override + public final Object con() { + return GEN_FLOAT.next(); + } + } + + static final class DoubleType extends PrimitiveType { + public static final DoubleType INSTANCE = new DoubleType(); + + @Override + public final String name() { return "double"; } + + @Override + public final Object con() { + return GEN_DOUBLE.next(); + } + } + + static final class BooleanType extends PrimitiveType { + public static final BooleanType INSTANCE = new BooleanType(); + + @Override + public final String name() { return "boolean"; } + + @Override + public final Object con() { + // TODO: generator for boolean? Could have different probabilities! + return RANDOM.nextInt() % 2 == 0; + } + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Type.java b/test/hotspot/jtreg/compiler/lib/template_library/Type.java index b3ab8adab7ea5..c7579816e5cce 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Type.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Type.java @@ -27,15 +27,6 @@ import compiler.lib.template_framework.Name; -import compiler.lib.template_library.types.ByteType; -import compiler.lib.template_library.types.CharType; -import compiler.lib.template_library.types.ShortType; -import compiler.lib.template_library.types.IntType; -import compiler.lib.template_library.types.LongType; -import compiler.lib.template_library.types.FloatType; -import compiler.lib.template_library.types.DoubleType; -import compiler.lib.template_library.types.BooleanType; - /** * The {@link Type} abstract class defines the basic functionalities that any {@link Type} * must provide, such as generating random expressions. @@ -44,16 +35,25 @@ */ public abstract class Type implements Name.Type { + public static final Type bytes() { return PrimitiveType.ByteType.INSTANCE; } + public static final Type chars() { return PrimitiveType.CharType.INSTANCE; } + public static final Type shorts() { return PrimitiveType.ShortType.INSTANCE; } + public static final Type ints() { return PrimitiveType.IntType.INSTANCE; } + public static final Type longs() { return PrimitiveType.LongType.INSTANCE; } + public static final Type floats() { return PrimitiveType.FloatType.INSTANCE; } + public static final Type doubles() { return PrimitiveType.DoubleType.INSTANCE; } + public static final Type booleans() { return PrimitiveType.BooleanType.INSTANCE; } + public static final List primitives() { return List.of( - ByteType.INSTANCE, - CharType.INSTANCE, - ShortType.INSTANCE, - IntType.INSTANCE, - LongType.INSTANCE, - FloatType.INSTANCE, - DoubleType.INSTANCE, - BooleanType.INSTANCE + bytes(), + chars(), + shorts(), + ints(), + longs(), + floats(), + doubles(), + booleans() ); } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java deleted file mode 100644 index 85ac74378d6c4..0000000000000 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/BooleanType.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package compiler.lib.template_library.types; - -import java.util.Random; -import jdk.test.lib.Utils; - -public final class BooleanType extends PrimitiveType { - public static final BooleanType INSTANCE = new BooleanType(); - private static final Random RANDOM = Utils.getRandomInstance(); - - @Override - public final String name() { return "boolean"; } - - @Override - public final Object con() { - // TODO: generator for boolean? Could have different probabilities! - return RANDOM.nextInt() % 2 == 0; - } -} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/ByteType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/ByteType.java deleted file mode 100644 index 3a70609433900..0000000000000 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/ByteType.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package compiler.lib.template_library.types; - -import compiler.lib.generators.Generators; -import compiler.lib.generators.RestrictableGenerator; - -public final class ByteType extends PrimitiveType { - public static final ByteType INSTANCE = new ByteType(); - private static final RestrictableGenerator GEN_BYTE = Generators.G.safeRestrict(Generators.G.ints(), Byte.MIN_VALUE, Byte.MAX_VALUE); - - @Override - public final String name() { return "byte"; } - - @Override - public final Object con() { - return "(byte)" + GEN_BYTE.next(); - } -} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/CharType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/CharType.java deleted file mode 100644 index 598eeaac98721..0000000000000 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/CharType.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package compiler.lib.template_library.types; - -import compiler.lib.generators.Generators; -import compiler.lib.generators.RestrictableGenerator; - -public final class CharType extends PrimitiveType { - public static final CharType INSTANCE = new CharType(); - private static final RestrictableGenerator GEN_CHAR = Generators.G.safeRestrict(Generators.G.ints(), Character.MIN_VALUE, Character.MAX_VALUE); - - @Override - public final String name() { return "char"; } - - @Override - public final Object con() { - return "(char)" + GEN_CHAR.next(); - } -} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java deleted file mode 100644 index f91894ef54d31..0000000000000 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/DoubleType.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package compiler.lib.template_library.types; - -import compiler.lib.generators.Generators; -import compiler.lib.generators.Generator; - -public final class DoubleType extends PrimitiveType { - public static final DoubleType INSTANCE = new DoubleType(); - private static final Generator GEN_DOUBLE = Generators.G.doubles(); - - @Override - public final String name() { return "double"; } - - @Override - public final Object con() { - return GEN_DOUBLE.next(); - } -} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java deleted file mode 100644 index bec1b3f55d8fe..0000000000000 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/FloatType.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package compiler.lib.template_library.types; - -import compiler.lib.generators.Generators; -import compiler.lib.generators.Generator; - -public final class FloatType extends PrimitiveType { - public static final FloatType INSTANCE = new FloatType(); - private static final Generator GEN_FLOAT = Generators.G.floats(); - - @Override - public final String name() { return "float"; } - - @Override - public final Object con() { - return GEN_FLOAT.next(); - } -} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java deleted file mode 100644 index 5462fd9784700..0000000000000 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/IntType.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package compiler.lib.template_library.types; - -import compiler.lib.generators.Generators; -import compiler.lib.generators.RestrictableGenerator; - -public final class IntType extends PrimitiveType { - public static final IntType INSTANCE = new IntType(); - private static final RestrictableGenerator GEN_INT = Generators.G.ints(); - - @Override - public final String name() { return "int"; } - - @Override - public final Object con() { - return GEN_INT.next(); - } -} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java deleted file mode 100644 index ae9befc7e5ae1..0000000000000 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/LongType.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package compiler.lib.template_library.types; - -import compiler.lib.generators.Generators; -import compiler.lib.generators.RestrictableGenerator; - -public final class LongType extends PrimitiveType { - public static final LongType INSTANCE = new LongType(); - private static final RestrictableGenerator GEN_LONG = Generators.G.longs(); - - @Override - public final String name() { return "long"; } - - @Override - public final Object con() { - return GEN_LONG.next(); - } -} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/PrimitiveType.java deleted file mode 100644 index 0ffc43e5013f9..0000000000000 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/PrimitiveType.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package compiler.lib.template_library.types; - -import compiler.lib.template_framework.Name; -import compiler.lib.template_library.Type; - -public abstract class PrimitiveType extends Type { - - @Override - public boolean isSubtypeOf(Name.Type other) { - // Primitives are only subtypes of their own Primitive type. - return this.getClass() == other.getClass(); - } -} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java b/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java deleted file mode 100644 index d538def3eff8e..0000000000000 --- a/test/hotspot/jtreg/compiler/lib/template_library/types/ShortType.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package compiler.lib.template_library.types; - -import compiler.lib.generators.Generators; -import compiler.lib.generators.RestrictableGenerator; - -public final class ShortType extends PrimitiveType { - public static final ShortType INSTANCE = new ShortType(); - private static final RestrictableGenerator GEN_SHORT = Generators.G.safeRestrict(Generators.G.ints(), Short.MIN_VALUE, Short.MAX_VALUE); - - @Override - public final String name() { return "short"; } - - @Override - public final Object con() { - return "(short)" + GEN_SHORT.next(); - } -} From 1b742e3b2f4ac69274d73643bc665c22ba01b30c Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 27 Feb 2025 19:55:06 +0100 Subject: [PATCH 128/212] problem list some operations --- .../jtreg/compiler/lib/template_library/Operations.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 9404596f797c4..ad0727151f345 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -132,7 +132,8 @@ public class Operations { new Operation.Unary(Type.ints(), "Integer.bitCount(", Type.ints(), ")"), new Operation.Binary(Type.ints(), "Integer.compare(", Type.ints(), ", ", Type.ints(), ")"), new Operation.Binary(Type.ints(), "Integer.compareUnsigned(", Type.ints(), ", ", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "Integer.compress(", Type.ints(), ", ", Type.ints(), ")"), + //new Operation.Binary(Type.ints(), "Integer.compress(", Type.ints(), ", ", Type.ints(), ")"), + // TODO: add back after JDK-8350896 new Operation.Binary(Type.ints(), "Integer.divideUnsigned(", Type.ints(), ", ", Type.ints(), ")"), new Operation.Binary(Type.ints(), "Integer.expand(", Type.ints(), ", ", Type.ints(), ")"), new Operation.Unary(Type.ints(), "Integer.highestOneBit(", Type.ints(), ")"), @@ -198,7 +199,8 @@ public class Operations { new Operation.Unary(Type.longs(), "Integer.toUnsignedLong(", Type.ints(), ")"), - new Operation.Binary(Type.longs(), "Long.compress(", Type.longs(), ", ", Type.longs(), ")"), + // new Operation.Binary(Type.longs(), "Long.compress(", Type.longs(), ", ", Type.longs(), ")"), + // TODO: add back after JDK-8350896 new Operation.Binary(Type.longs(), "Long.divideUnsigned(", Type.longs(), ", ", Type.longs(), ")"), new Operation.Binary(Type.longs(), "Long.expand(", Type.longs(), ", ", Type.longs(), ")"), new Operation.Unary(Type.longs(), "Long.highestOneBit(", Type.longs(), ")"), @@ -235,7 +237,8 @@ public class Operations { new Operation.Binary(Type.floats(), "(", Type.floats(), " / ", Type.floats(), ")"), new Operation.Binary(Type.floats(), "(", Type.floats(), " % ", Type.floats(), ")"), - new Operation.Unary(Type.floats(), "Float.float16ToFloat(", Type.shorts(), ")"), + //new Operation.Unary(Type.floats(), "Float.float16ToFloat(", Type.shorts(), ")"), + // TODO: add back after JDK-8350835 new Operation.Unary(Type.floats(), "Float.intBitsToFloat(", Type.ints(), ")"), new Operation.Binary(Type.floats(), "Float.max(", Type.floats(), ", ", Type.floats(), ")"), new Operation.Binary(Type.floats(), "Float.min(", Type.floats(), ", ", Type.floats(), ")"), From ff450685bc02d0bc32bc18898ff90a09e398f1ad Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 27 Feb 2025 20:07:09 +0100 Subject: [PATCH 129/212] add constants into expression --- .../lib/template_library/Expression.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index 253e1aa51f5fb..f58f96baae89d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -109,13 +109,21 @@ private static final ExpressionGenerator expressionGenerator(Type resultType, Ha private static final ExpressionGeneratorStep expressionGeneratorStep(Type resultType, HashSet allowedTypes, int maxDepth, List types) { List ops = Operations.PRIMITIVE_OPERATIONS.stream().filter(o -> o.matchesTypes(resultType, allowedTypes)).toList(); if (maxDepth <= 0 || ops.isEmpty() || RANDOM.nextInt(2 * maxDepth) == 0) { - // Remember which type we need to fill the ith argument with. - int i = types.size(); - types.add(resultType); - return (List tokens, List args) -> { - // Extract the ith argument. - tokens.add(args.get(i)); - }; + if (RANDOM.nextInt(3) == 0) { + // Fill with random constant value. + Object c = resultType.con(); + return (List tokens, List args) -> { + tokens.add(c); + }; + } else { + // Remember which type we need to fill the ith argument with. + int i = types.size(); + types.add(resultType); + return (List tokens, List args) -> { + // Extract the ith argument. + tokens.add(args.get(i)); + }; + } } switch (Library.choice(ops)) { case Operation.Unary(Type r, String s0, Type t0, String s1) -> { From 9e87707acbd5d0d3b99b8f29bb8fb6c811fa667a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 28 Feb 2025 09:23:03 +0100 Subject: [PATCH 130/212] stub for Vector API types --- .../lib/template_library/Expression.java | 4 +- .../lib/template_library/PrimitiveType.java | 78 ++++++++- .../compiler/lib/template_library/Type.java | 70 +++++--- .../lib/template_library/VectorAPIType.java | 84 ++++++++++ .../examples/TestFuzzExpression.java | 10 +- .../examples/TestFuzzVectorAPI.java | 152 ++++++++++++++++++ 6 files changed, 370 insertions(+), 28 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index f58f96baae89d..0b36000c84ce3 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -85,8 +85,8 @@ private interface ExpressionGeneratorStep { void addTokens(List tokens, List args); } - public static final Expression make(Type resultType, List allowedTypes, int maxDepth) { - HashSet allowedTypesSet = new HashSet(allowedTypes); + public static final Expression make(Type resultType, List allowedTypes, int maxDepth) { + HashSet allowedTypesSet = new HashSet(allowedTypes); List types = new ArrayList(); ExpressionGenerator generator = expressionGenerator(resultType, allowedTypesSet, maxDepth, types); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_library/PrimitiveType.java index 7899b08690766..2090e26a4efe7 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/PrimitiveType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/PrimitiveType.java @@ -32,7 +32,7 @@ import compiler.lib.template_framework.Name; -abstract class PrimitiveType extends Type { +public abstract class PrimitiveType extends Type { 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); @@ -48,16 +48,29 @@ public boolean isSubtypeOf(Name.Type other) { return this.getClass() == other.getClass(); } + public abstract int sizeInBits(); + public abstract String boxedTypeName(); + public abstract String vectorAPITypeName(); + static final class ByteType extends PrimitiveType { public static final ByteType INSTANCE = new ByteType(); @Override public final String name() { return "byte"; } + @Override + public String boxedTypeName() { return "Byte"; } + + @Override + public String vectorAPITypeName() { return "ByteVector"; } + @Override public final Object con() { return "(byte)" + GEN_BYTE.next(); } + + @Override + public int sizeInBits() { return 8; } } static final class CharType extends PrimitiveType { @@ -66,10 +79,19 @@ static final class CharType extends PrimitiveType { @Override public final String name() { return "char"; } + @Override + public String boxedTypeName() { return "Character"; } + + @Override + public String vectorAPITypeName() { throw new UnsupportedOperationException("VectorAPI has no char vector type."); } + @Override public final Object con() { return "(char)" + GEN_CHAR.next(); } + + @Override + public int sizeInBits() { return 16; } } static final class ShortType extends PrimitiveType { @@ -78,10 +100,19 @@ static final class ShortType extends PrimitiveType { @Override public final String name() { return "short"; } + @Override + public String boxedTypeName() { return "Short"; } + + @Override + public String vectorAPITypeName() { return "ShortVector"; } + @Override public final Object con() { return "(short)" + GEN_SHORT.next(); } + + @Override + public int sizeInBits() { return 16; } } static final class IntType extends PrimitiveType { @@ -90,10 +121,19 @@ static final class IntType extends PrimitiveType { @Override public final String name() { return "int"; } + @Override + public String boxedTypeName() { return "Integer"; } + + @Override + public String vectorAPITypeName() { return "IntVector"; } + @Override public final Object con() { return GEN_INT.next(); } + + @Override + public int sizeInBits() { return 32; } } static final class LongType extends PrimitiveType { @@ -102,10 +142,19 @@ static final class LongType extends PrimitiveType { @Override public final String name() { return "long"; } + @Override + public String boxedTypeName() { return "Long"; } + + @Override + public String vectorAPITypeName() { return "LongVector"; } + @Override public final Object con() { return GEN_LONG.next(); } + + @Override + public int sizeInBits() { return 64; } } static final class FloatType extends PrimitiveType { @@ -114,10 +163,19 @@ static final class FloatType extends PrimitiveType { @Override public final String name() { return "float"; } + @Override + public String boxedTypeName() { return "Float"; } + + @Override + public String vectorAPITypeName() { return "FloatVector"; } + @Override public final Object con() { return GEN_FLOAT.next(); } + + @Override + public int sizeInBits() { return 32; } } static final class DoubleType extends PrimitiveType { @@ -126,10 +184,19 @@ static final class DoubleType extends PrimitiveType { @Override public final String name() { return "double"; } + @Override + public String boxedTypeName() { return "Double"; } + + @Override + public String vectorAPITypeName() { return "DoubleVector"; } + @Override public final Object con() { return GEN_DOUBLE.next(); } + + @Override + public int sizeInBits() { return 64; } } static final class BooleanType extends PrimitiveType { @@ -138,10 +205,19 @@ static final class BooleanType extends PrimitiveType { @Override public final String name() { return "boolean"; } + @Override + public String boxedTypeName() { return "Boolean"; } + + @Override + public String vectorAPITypeName() { throw new UnsupportedOperationException("VectorAPI has no boolean vector type."); } + @Override public final Object con() { // TODO: generator for boolean? Could have different probabilities! return RANDOM.nextInt() % 2 == 0; } + + @Override + public int sizeInBits() { throw new UnsupportedOperationException("boolean does not have number of bits"); } } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Type.java b/test/hotspot/jtreg/compiler/lib/template_library/Type.java index c7579816e5cce..a606dfee7af58 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Type.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Type.java @@ -35,27 +35,57 @@ */ public abstract class Type implements Name.Type { - public static final Type bytes() { return PrimitiveType.ByteType.INSTANCE; } - public static final Type chars() { return PrimitiveType.CharType.INSTANCE; } - public static final Type shorts() { return PrimitiveType.ShortType.INSTANCE; } - public static final Type ints() { return PrimitiveType.IntType.INSTANCE; } - public static final Type longs() { return PrimitiveType.LongType.INSTANCE; } - public static final Type floats() { return PrimitiveType.FloatType.INSTANCE; } - public static final Type doubles() { return PrimitiveType.DoubleType.INSTANCE; } - public static final Type booleans() { return PrimitiveType.BooleanType.INSTANCE; } + public static final PrimitiveType bytes() { return PrimitiveType.ByteType.INSTANCE; } + public static final PrimitiveType chars() { return PrimitiveType.CharType.INSTANCE; } + public static final PrimitiveType shorts() { return PrimitiveType.ShortType.INSTANCE; } + public static final PrimitiveType ints() { return PrimitiveType.IntType.INSTANCE; } + public static final PrimitiveType longs() { return PrimitiveType.LongType.INSTANCE; } + public static final PrimitiveType floats() { return PrimitiveType.FloatType.INSTANCE; } + public static final PrimitiveType doubles() { return PrimitiveType.DoubleType.INSTANCE; } + public static final PrimitiveType booleans() { return PrimitiveType.BooleanType.INSTANCE; } - public static final List primitives() { - return List.of( - bytes(), - chars(), - shorts(), - ints(), - longs(), - floats(), - doubles(), - booleans() - ); - } + public static final List PRIMITIVE_TYPES = List.of( + bytes(), + chars(), + shorts(), + ints(), + longs(), + floats(), + doubles(), + booleans() + ); + + public static final List VECTOR_API_TYPES = List.of( + VectorAPIType.BYTE_64, + VectorAPIType.BYTE_128, + VectorAPIType.BYTE_256, + VectorAPIType.BYTE_512, + + VectorAPIType.SHORT_64, + VectorAPIType.SHORT_128, + VectorAPIType.SHORT_256, + VectorAPIType.SHORT_512, + + VectorAPIType.INT_64, + VectorAPIType.INT_128, + VectorAPIType.INT_256, + VectorAPIType.INT_512, + + VectorAPIType.LONG_64, + VectorAPIType.LONG_128, + VectorAPIType.LONG_256, + VectorAPIType.LONG_512, + + VectorAPIType.FLOAT_64, + VectorAPIType.FLOAT_128, + VectorAPIType.FLOAT_256, + VectorAPIType.FLOAT_512, + + VectorAPIType.DOUBLE_64, + VectorAPIType.DOUBLE_128, + VectorAPIType.DOUBLE_256, + VectorAPIType.DOUBLE_512 + ); /** * Returns name of the type that can be used in Java code. diff --git a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java new file mode 100644 index 0000000000000..3b271c3d4861e --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java @@ -0,0 +1,84 @@ +/* + * 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_library; + +import compiler.lib.template_framework.Name; + +class VectorAPIType extends Type { + public static final VectorAPIType BYTE_64 = new VectorAPIType(Type.bytes(), 8); + public static final VectorAPIType BYTE_128 = new VectorAPIType(Type.bytes(), 16); + public static final VectorAPIType BYTE_256 = new VectorAPIType(Type.bytes(), 32); + public static final VectorAPIType BYTE_512 = new VectorAPIType(Type.bytes(), 64); + + public static final VectorAPIType SHORT_64 = new VectorAPIType(Type.shorts(), 4); + public static final VectorAPIType SHORT_128 = new VectorAPIType(Type.shorts(), 8); + public static final VectorAPIType SHORT_256 = new VectorAPIType(Type.shorts(), 16); + public static final VectorAPIType SHORT_512 = new VectorAPIType(Type.shorts(), 32); + + public static final VectorAPIType INT_64 = new VectorAPIType(Type.ints(), 2); + public static final VectorAPIType INT_128 = new VectorAPIType(Type.ints(), 4); + public static final VectorAPIType INT_256 = new VectorAPIType(Type.ints(), 8); + public static final VectorAPIType INT_512 = new VectorAPIType(Type.ints(), 16); + + public static final VectorAPIType LONG_64 = new VectorAPIType(Type.longs(), 1); + public static final VectorAPIType LONG_128 = new VectorAPIType(Type.longs(), 2); + public static final VectorAPIType LONG_256 = new VectorAPIType(Type.longs(), 4); + public static final VectorAPIType LONG_512 = new VectorAPIType(Type.longs(), 8); + + public static final VectorAPIType FLOAT_64 = new VectorAPIType(Type.floats(), 2); + public static final VectorAPIType FLOAT_128 = new VectorAPIType(Type.floats(), 4); + public static final VectorAPIType FLOAT_256 = new VectorAPIType(Type.floats(), 8); + public static final VectorAPIType FLOAT_512 = new VectorAPIType(Type.floats(), 16); + + public static final VectorAPIType DOUBLE_64 = new VectorAPIType(Type.doubles(), 1); + public static final VectorAPIType DOUBLE_128 = new VectorAPIType(Type.doubles(), 2); + public static final VectorAPIType DOUBLE_256 = new VectorAPIType(Type.doubles(), 4); + public static final VectorAPIType DOUBLE_512 = new VectorAPIType(Type.doubles(), 8); + + public final PrimitiveType elementType; + public final int length; // number of lanes (i.e. elements) + public final String vectorType; + public final String species; + + VectorAPIType(PrimitiveType elementType, int length) { + this.elementType = elementType; + this.length = length; + this.vectorType = elementType.vectorAPITypeName(); + this.species = vectorType + ".SPECIES_" + (elementType.sizeInBits() * length); + } + + @Override + public boolean isSubtypeOf(Name.Type other) { + // TODO: re-evaluate + return this == other; + } + + @Override + public final String name() { return vectorType; } + + @Override + public final Object con() { + return "/* TODO */ null"; + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index ed1ab62747cb9..d6b4de7a4d865 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -98,7 +98,7 @@ public static String generate(CompileFramework comp) { // Note: we are using the "expression" in two separate methods, so we cannot generate any // fields with "def", as we would have to generate them identically twice. var template1 = Template.make("type", (Type type)-> { - Expression expression = Expression.make(type, Type.primitives(), 4); + Expression expression = Expression.make(type, Type.PRIMITIVE_TYPES, 4); List argValues = expression.randomArgValues(); List def = argValues.stream().map(v -> v.defTokens()).toList(); List use = argValues.stream().map(v -> v.useTokens()).toList(); @@ -154,7 +154,7 @@ public static String generate(CompileFramework comp) { // hook are both already set before we call "expression.withRandomArgs", and so that // we know we can generate fields and local variables. var template2Body = Template.make("type", (Type type)-> { - Expression expression = Expression.make(type, Type.primitives(), 4); + Expression expression = Expression.make(type, Type.PRIMITIVE_TYPES, 4); return body( """ try { @@ -210,7 +210,7 @@ public static String generate(CompileFramework comp) { // and storing to an output array. var template3 = Template.make("type", (Type type)-> { int size = 10_000; // TODO: randomize - Expression expression = Expression.make(type, Type.primitives(), 4); + Expression expression = Expression.make(type, Type.PRIMITIVE_TYPES, 4); List types = expression.types(); List arrayDefinitions = new ArrayList<>(); List args = new ArrayList<>(); @@ -263,10 +263,10 @@ public static String generate(CompileFramework comp) { // TODO: hand-unrollling case - // Use template1 100 times with every type. + // Now use the templates and add them into the IRTestClass. List templates = new ArrayList<>(); templates.add(Library.arrayFillMethods()); - for (Type type : Type.primitives()) { + for (Type type : Type.PRIMITIVE_TYPES) { for (int i = 0; i < 20; i++) { templates.add(template1.withArgs(type)); } for (int i = 0; i < 20; i++) { templates.add(template2.withArgs(type)); } for (int i = 0; i < 5; i++) { templates.add(template3.withArgs(type)); } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java new file mode 100644 index 0000000000000..21128e35290f2 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java @@ -0,0 +1,152 @@ +/* + * 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 + * @summary Test the Template Library's expression generation for the Vector API. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @compile ../../../compiler/lib/ir_framework/TestFramework.java + * @compile ../../../compiler/lib/generators/Generators.java + * @compile ../../../compiler/lib/verify/Verify.java + * @run driver template_library.examples.TestFuzzVectorAPI + */ + +package template_library.examples; + +import java.util.List; +import java.util.ArrayList; + +import compiler.lib.compile_framework.CompileFramework; + +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateWithArgs; +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_library.Library; +import compiler.lib.template_library.IRTestClass; +import compiler.lib.template_library.Type; +import compiler.lib.template_library.Expression; +import compiler.lib.template_library.Value; + +/** + * TODO + * See {@link TestFuzzExpression} for more explanation about the structure of the test. + */ +public class TestFuzzVectorAPI { + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("p.xyz.InnerTest", generate(comp)); + + // Compile the source file. + comp.compile(); + + // Object ret = p.xyz.InnterTest.main(); + Object ret = comp.invoke("p.xyz.InnerTest", "main", new Object[] {}); + } + + // Generate a source Java file as String + public static String generate(CompileFramework comp) { + // Create the info required for the test class. + // It is imporant that we pass the classpath to the Test-VM, so that it has access + // to all compiled classes. + IRTestClass.Info info = new IRTestClass.Info(comp.getEscapedClassPathOfCompiledClasses(), + "p.xyz", "InnerTest", + List.of("compiler.lib.generators.*", + "compiler.lib.verify.*", + "jdk.incubator.vector.*")); + + // Example 1: + // We only use the "expression" once, and so we can conveniently just run it with + // random arguments. Those may also generate their own fields under the hood. + // + // Note: we put the expression in a separate Template so that the method and class + // hook are both already set before we call "expression.withRandomArgs", and so that + // we know we can generate fields and local variables. + var template1Body = Template.make("type", (Type type)-> { + Expression expression = Expression.make(type, Type.VECTOR_API_TYPES, 4); + return body( + """ + try { + """, + " return ", expression.withRandomArgs(), ";\n", + """ + } catch (Exception e) { + return e; + } + """ + ); + }); + var template1 = Template.make("type", (Type type)-> { + return body( + """ + // --- $test start --- + // Using $GOLD + // type: #type + """, + Library.CLASS_HOOK.set( + """ + + static final Object $GOLD = $test(); + + @Test + public static Object $test() { + """, + Library.METHOD_HOOK.set( + template1Body.withArgs(type) + ), + """ + } + + @Check(test = "$test") + public static void $check(Object result) { + Verify.checkEQ(result, $GOLD); + } + + // --- $test end --- + """ + ) + ); + }); + + var defineArray = Template.make("type", "name", "size", (Type type, String name, Integer size) -> body( + """ + public static #type[] #name = fill(new #type[#size]); + """ + )); + + // Now use the templates and add them into the IRTestClass. + List templates = new ArrayList<>(); + templates.add(Library.arrayFillMethods()); + for (Type type : Type.VECTOR_API_TYPES) { + for (int i = 0; i < 20; i++) { templates.add(template1.withArgs(type)); } + } + return IRTestClass.TEMPLATE.withArgs(info, templates).render(); + } +} From 9a60be3c27fcb4804b5b552ce8684ebf571e8078 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 28 Feb 2025 11:28:12 +0100 Subject: [PATCH 131/212] generate vectors, and verify them --- .../lib/template_library/VectorAPIType.java | 10 +- .../jtreg/compiler/lib/verify/Verify.java | 118 +++++++++++++++++- 2 files changed, 126 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java index 3b271c3d4861e..f3948c373b68e 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java @@ -23,9 +23,15 @@ package compiler.lib.template_library; +import java.util.List; +import java.util.Random; +import jdk.test.lib.Utils; + import compiler.lib.template_framework.Name; class VectorAPIType extends Type { + private static final Random RANDOM = Utils.getRandomInstance(); + public static final VectorAPIType BYTE_64 = new VectorAPIType(Type.bytes(), 8); public static final VectorAPIType BYTE_128 = new VectorAPIType(Type.bytes(), 16); public static final VectorAPIType BYTE_256 = new VectorAPIType(Type.bytes(), 32); @@ -79,6 +85,8 @@ public boolean isSubtypeOf(Name.Type other) { @Override public final Object con() { - return "/* TODO */ null"; + int r = RANDOM.nextInt(32); + if (r == 0) { return "null"; } + return List.of(vectorType, ".broadcast(", species, ", ", elementType.con(), ")"); } } diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index 3c0878203955d..4764a8340b00a 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -25,12 +25,14 @@ import java.util.Optional; import java.lang.foreign.*; +import jdk.incubator.vector.*; /** * The {@link Verify} class provides a single {@link Verify#checkEQ} static method, which recursively * compares the two {@link Object}s by value. It deconstructs {@link Object[]}, compares boxed primitive * types, compares the content of arrays and {@link MemorySegment}s, and checks that the messages of two - * {@link Exception}s are equal. + * {@link Exception}s are equal. We also check for equivalent content in {@link Vector}s from the Vector + * API. * * When a comparison fail, then methods print helpful messages, before throwing a {@link VerifyException}. */ @@ -96,6 +98,12 @@ private static void checkEQ(Object a, Object b, String context) { case double[] x -> checkEQimpl(x, (double[])b, context); case boolean[] x -> checkEQimpl(x, (boolean[])b, context); case MemorySegment x -> checkEQimpl(x, (MemorySegment) b, context); + case ByteVector x -> checkEQimpl(x, (ByteVector) b, context); + case ShortVector x -> checkEQimpl(x, (ShortVector) b, context); + case IntVector x -> checkEQimpl(x, (IntVector) b, context); + case LongVector x -> checkEQimpl(x, (LongVector) b, context); + case FloatVector x -> checkEQimpl(x, (FloatVector) b, context); + case DoubleVector x -> checkEQimpl(x, (DoubleVector) b, context); case Exception x -> checkEQimpl(x, (Exception) b, context); default -> { System.err.println("ERROR: Verify.checkEQ failed: type not supported: " + ca.getName()); @@ -332,6 +340,114 @@ private static void checkEQimpl(boolean[] a, boolean[] b, String context) { } } + /** + * Verify that the content of two ShortVector is identical. + */ + private static void checkEQimpl(ShortVector a, ShortVector b, String context) { + if (a.length() != b.length()) { + System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length() + " vs " + b.length() + " " + context); + throw new VerifyException("ShortVector length mismatch."); + } + + for (int i = 0; i < a.length(); i++) { + if (a.lane(i) != b.lane(i)) { + System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a.lane(i) + " vs " + b.lane(i) + " " + context); + throw new VerifyException("ShortVector value mismatch."); + } + } + } + + /** + * Verify that the content of two IntVector is identical. + */ + private static void checkEQimpl(IntVector a, IntVector b, String context) { + if (a.length() != b.length()) { + System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length() + " vs " + b.length() + " " + context); + throw new VerifyException("IntVector length mismatch."); + } + + for (int i = 0; i < a.length(); i++) { + if (a.lane(i) != b.lane(i)) { + System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a.lane(i) + " vs " + b.lane(i) + " " + context); + throw new VerifyException("IntVector value mismatch."); + } + } + } + + /** + * Verify that the content of two ByteVector is identical. + */ + private static void checkEQimpl(LongVector a, LongVector b, String context) { + if (a.length() != b.length()) { + System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length() + " vs " + b.length() + " " + context); + throw new VerifyException("LongVector length mismatch."); + } + + for (int i = 0; i < a.length(); i++) { + if (a.lane(i) != b.lane(i)) { + System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a.lane(i) + " vs " + b.lane(i) + " " + context); + throw new VerifyException("LongVector value mismatch."); + } + } + } + + /** + * Verify that the content of two ByteVector is identical. + * Ideally, we would want to assert that the Float.floatToRawIntBits are identical. + * But the Java spec allows us to return different bits for a NaN, which allows swapping the inputs + * of an add or mul (NaN1 * NaN2 does not have same bits as NaN2 * NaN1, because the multiplication + * of two NaN values should always return the first of the two). So we verify that we have the same bit + * pattern in all cases, except for NaN we project to the canonical NaN, using Float.floatToIntBits. + */ + private static void checkEQimpl(FloatVector a, FloatVector b, String context) { + if (a.length() != b.length()) { + System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length() + " vs " + b.length() + " " + context); + throw new VerifyException("FloatVector length mismatch."); + } + + for (int i = 0; i < a.length(); i++) { + if (Float.floatToIntBits(a.lane(i)) != Float.floatToIntBits(b.lane(i))) { + System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a.lane(i) + " vs " + b.lane(i) + " " + context); + throw new VerifyException("FloatVector value mismatch."); + } + } + } + + /** + * Verify that the content of two ByteVector is identical. + * Same issue with NaN as above for floats. + */ + private static void checkEQimpl(DoubleVector a, DoubleVector b, String context) { + if (a.length() != b.length()) { + System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length() + " vs " + b.length() + " " + context); + throw new VerifyException("DoubleVector length mismatch."); + } + + for (int i = 0; i < a.length(); i++) { + if (Double.doubleToLongBits(a.lane(i)) != Double.doubleToLongBits(b.lane(i))) { + System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a.lane(i) + " vs " + b.lane(i) + " " + context); + throw new VerifyException("DoubleVector value mismatch."); + } + } + } + + /** + * Verify that the content of two ByteVector is identical. + */ + private static void checkEQimpl(ByteVector a, ByteVector b, String context) { + if (a.length() != b.length()) { + System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length() + " vs " + b.length() + " " + context); + throw new VerifyException("ByteVector length mismatch."); + } + + for (int i = 0; i < a.length(); i++) { + if (a.lane(i) != b.lane(i)) { + System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a.lane(i) + " vs " + b.lane(i) + " " + context); + throw new VerifyException("ByteVector value mismatch."); + } + } + } + /** * Verify that the content of two Object arrays is identical, recursively: * every element is compared with checkEQimpl for the corresponding type. From da1f140775ad0ab5ef47e180e07ed3f2935a50ed Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 28 Feb 2025 11:58:48 +0100 Subject: [PATCH 132/212] wip vector api types --- .../lib/template_library/Operations.java | 12 ++++++- .../compiler/lib/template_library/Type.java | 32 +++++++++++++++---- .../lib/template_library/VectorAPIType.java | 4 +-- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index ad0727151f345..f7c1c96bb7167 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -24,9 +24,10 @@ package compiler.lib.template_library; import java.util.List; +import java.util.ArrayList; import java.util.stream.Stream; -public class Operations { +final class Operations { private static final List BYTE_OPERATIONS = List.of( // Note: the standard integer arithmetic operations are only defined for int/long. @@ -308,4 +309,13 @@ public class Operations { DOUBLE_OPERATIONS, BOOLEAN_OPERATIONS ).flatMap((List l) -> l.stream()).toList(); + + private static final List generateVectorAPIOperations() { + List ops = new ArrayList(); + + // Ensure the list is immutable. + return List.copyOf(ops); + } + + public static final List VECTOR_API_OPERATIONS = generateVectorAPIOperations(); } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Type.java b/test/hotspot/jtreg/compiler/lib/template_library/Type.java index a606dfee7af58..6ad0ad2efb7f8 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Type.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Type.java @@ -24,6 +24,7 @@ package compiler.lib.template_library; import java.util.List; +import java.util.stream.Stream; import compiler.lib.template_framework.Name; @@ -55,38 +56,57 @@ public abstract class Type implements Name.Type { booleans() ); - public static final List VECTOR_API_TYPES = List.of( + public static final List VECTOR_API_BYTE_TYPES = List.of( VectorAPIType.BYTE_64, VectorAPIType.BYTE_128, VectorAPIType.BYTE_256, - VectorAPIType.BYTE_512, + VectorAPIType.BYTE_512 + ); + public static final List VECTOR_API_SHORT_TYPES = List.of( VectorAPIType.SHORT_64, VectorAPIType.SHORT_128, VectorAPIType.SHORT_256, - VectorAPIType.SHORT_512, + VectorAPIType.SHORT_512 + ); + public static final List VECTOR_API_INT_TYPES = List.of( VectorAPIType.INT_64, VectorAPIType.INT_128, VectorAPIType.INT_256, - VectorAPIType.INT_512, + VectorAPIType.INT_512 + ); + public static final List VECTOR_API_LONG_TYPES = List.of( VectorAPIType.LONG_64, VectorAPIType.LONG_128, VectorAPIType.LONG_256, - VectorAPIType.LONG_512, + VectorAPIType.LONG_512 + ); + public static final List VECTOR_API_FLOAT_TYPES = List.of( VectorAPIType.FLOAT_64, VectorAPIType.FLOAT_128, VectorAPIType.FLOAT_256, - VectorAPIType.FLOAT_512, + VectorAPIType.FLOAT_512 + ); + public static final List VECTOR_API_DOUBLE_TYPES = List.of( VectorAPIType.DOUBLE_64, VectorAPIType.DOUBLE_128, VectorAPIType.DOUBLE_256, VectorAPIType.DOUBLE_512 ); + public static final List VECTOR_API_TYPES = Stream.of( + VECTOR_API_BYTE_TYPES, + VECTOR_API_SHORT_TYPES, + VECTOR_API_INT_TYPES, + VECTOR_API_LONG_TYPES, + VECTOR_API_FLOAT_TYPES, + VECTOR_API_DOUBLE_TYPES + ).flatMap((List l) -> l.stream()).toList(); + /** * Returns name of the type that can be used in Java code. * diff --git a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java index f3948c373b68e..ac013ba29d558 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java @@ -29,7 +29,7 @@ import compiler.lib.template_framework.Name; -class VectorAPIType extends Type { +public class VectorAPIType extends Type { private static final Random RANDOM = Utils.getRandomInstance(); public static final VectorAPIType BYTE_64 = new VectorAPIType(Type.bytes(), 8); @@ -67,7 +67,7 @@ class VectorAPIType extends Type { public final String vectorType; public final String species; - VectorAPIType(PrimitiveType elementType, int length) { + private VectorAPIType(PrimitiveType elementType, int length) { this.elementType = elementType; this.length = length; this.vectorType = elementType.vectorAPITypeName(); From 43c2c7fcdf618bf8fbe2cb2b7a1d6a7b6dd2bb86 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 3 Mar 2025 09:07:15 +0100 Subject: [PATCH 133/212] wip vector api, and small expression refactoring --- .../lib/template_library/Expression.java | 27 ++++++------ .../lib/template_library/Library.java | 10 +++++ .../lib/template_library/Operation.java | 44 +++++++++++++------ .../lib/template_library/Operations.java | 9 ++++ .../compiler/lib/template_library/Type.java | 5 +++ .../lib/template_library/VectorAPIType.java | 3 +- .../examples/TestFuzzVectorAPI.java | 2 +- 7 files changed, 72 insertions(+), 28 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index 0b36000c84ce3..4e3415d38d30a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -87,9 +87,10 @@ private interface ExpressionGeneratorStep { public static final Expression make(Type resultType, List allowedTypes, int maxDepth) { HashSet allowedTypesSet = new HashSet(allowedTypes); + List ops = Operations.ALL_BUILTIN_OPERATIONS.stream().filter(o -> o.matchesTypes(allowedTypesSet)).toList(); List types = new ArrayList(); - ExpressionGenerator generator = expressionGenerator(resultType, allowedTypesSet, maxDepth, types); + ExpressionGenerator generator = expressionGenerator(resultType, ops, maxDepth, types); var template = Template.make("args", (List args) -> body( generator.tokens(args) @@ -97,8 +98,8 @@ public static final Expression make(Type resultType, List al return new Expression(template, types); } - private static final ExpressionGenerator expressionGenerator(Type resultType, HashSet allowedTypes, int maxDepth, List types) { - ExpressionGeneratorStep step = expressionGeneratorStep(resultType, allowedTypes, maxDepth, types); + private static final ExpressionGenerator expressionGenerator(Type resultType, List ops, int maxDepth, List types) { + ExpressionGeneratorStep step = expressionGeneratorStep(resultType, ops, maxDepth, types); return (List args) -> { List tokens = new ArrayList(); step.addTokens(tokens, args); @@ -106,9 +107,9 @@ private static final ExpressionGenerator expressionGenerator(Type resultType, Ha }; } - private static final ExpressionGeneratorStep expressionGeneratorStep(Type resultType, HashSet allowedTypes, int maxDepth, List types) { - List ops = Operations.PRIMITIVE_OPERATIONS.stream().filter(o -> o.matchesTypes(resultType, allowedTypes)).toList(); - if (maxDepth <= 0 || ops.isEmpty() || RANDOM.nextInt(2 * maxDepth) == 0) { + private static final ExpressionGeneratorStep expressionGeneratorStep(Type resultType, List ops, int maxDepth, List types) { + List resultTypeOps = ops.stream().filter(o -> o.matchesReturnType(resultType)).toList(); + if (maxDepth <= 0 || resultTypeOps.isEmpty() || RANDOM.nextInt(2 * maxDepth) == 0) { if (RANDOM.nextInt(3) == 0) { // Fill with random constant value. Object c = resultType.con(); @@ -125,9 +126,9 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result }; } } - switch (Library.choice(ops)) { + switch (Library.choice(resultTypeOps)) { case Operation.Unary(Type r, String s0, Type t0, String s1) -> { - ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, allowedTypes, maxDepth-1, types); + ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, types); return (List tokens, List args) -> { tokens.add(s0); step0.addTokens(tokens, args); @@ -135,8 +136,8 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result }; } case Operation.Binary(Type r, String s0, Type t0, String s1, Type t1, String s2) -> { - ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, allowedTypes, maxDepth-1, types); - ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, allowedTypes, maxDepth-1, types); + ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, types); + ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, ops, maxDepth-1, types); return (List tokens, List args) -> { tokens.add(s0); step0.addTokens(tokens, args); @@ -146,9 +147,9 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result }; } case Operation.Ternary(Type r, String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3) -> { - ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, allowedTypes, maxDepth-1, types); - ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, allowedTypes, maxDepth-1, types); - ExpressionGeneratorStep step2 = expressionGeneratorStep(t1, allowedTypes, maxDepth-1, types); + ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, types); + ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, ops, maxDepth-1, types); + ExpressionGeneratorStep step2 = expressionGeneratorStep(t1, ops, maxDepth-1, types); return (List tokens, List args) -> { tokens.add(s0); step0.addTokens(tokens, args); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Library.java b/test/hotspot/jtreg/compiler/lib/template_library/Library.java index ac6d2866bb255..b20515642252c 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Library.java @@ -24,6 +24,7 @@ package compiler.lib.template_library; import java.util.List; +import java.util.ArrayList; import java.util.Random; import jdk.test.lib.Utils; @@ -55,6 +56,15 @@ static T choice(List list) { return list.get(i); } + static final List concat(List a, List b) { + List list = new ArrayList(); + list.addAll(a); + list.addAll(b); + + // Ensure the list is immutable. + return List.copyOf(list); + } + public static TemplateWithArgs defineField(Name name, boolean isStatic, Object valueToken) { var define = Template.make(() -> body( let("static", isStatic ? "static" : ""), diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operation.java b/test/hotspot/jtreg/compiler/lib/template_library/Operation.java index 191df6c8a5e8a..09ca4f2a8cb48 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operation.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operation.java @@ -33,32 +33,50 @@ public sealed interface Operation permits Operation.Unary, * Check if input types are in the set. * Check that return type is subtype of the desired type. */ - public abstract boolean matchesTypes(Type returnType, HashSet argumentTypes); + public abstract boolean matchesReturnType(Type returnType); + + public abstract boolean matchesTypes(HashSet types); public static record Unary(Type r, String s0, Type t0, String s1) implements Operation { @Override - public boolean matchesTypes(Type returnType, HashSet argumentTypes) { - return r.isSubtypeOf(returnType) && - argumentTypes.contains(t0); + public boolean matchesReturnType(Type returnType) { + return r.isSubtypeOf(returnType); + } + + + @Override + public boolean matchesTypes(HashSet types) { + return types.contains(r) && + types.contains(t0); } } public static record Binary(Type r, String s0, Type t0, String s1, Type t1, String s2) implements Operation { @Override - public boolean matchesTypes(Type returnType, HashSet argumentTypes) { - return r.isSubtypeOf(returnType) && - argumentTypes.contains(t0) && - argumentTypes.contains(t1); + public boolean matchesReturnType(Type returnType) { + return r.isSubtypeOf(returnType); + } + + @Override + public boolean matchesTypes(HashSet types) { + return types.contains(r) && + types.contains(t0) && + types.contains(t1); } } public static record Ternary(Type r, String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3) implements Operation { @Override - public boolean matchesTypes(Type returnType, HashSet argumentTypes) { - return r.isSubtypeOf(returnType) && - argumentTypes.contains(t0) && - argumentTypes.contains(t1) && - argumentTypes.contains(t2); + public boolean matchesReturnType(Type returnType) { + return r.isSubtypeOf(returnType); + } + + @Override + public boolean matchesTypes(HashSet types) { + return types.contains(r) && + types.contains(t0) && + types.contains(t1) && + types.contains(t2); } } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index f7c1c96bb7167..1cad1079ab48a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -313,9 +313,18 @@ final class Operations { private static final List generateVectorAPIOperations() { List ops = new ArrayList(); + for (var type : Type.VECTOR_API_TYPES) { + ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(", type.elementType, ")")); + } + // Ensure the list is immutable. return List.copyOf(ops); } public static final List VECTOR_API_OPERATIONS = generateVectorAPIOperations(); + + public static final List ALL_BUILTIN_OPERATIONS = Library.concat( + PRIMITIVE_OPERATIONS, + VECTOR_API_OPERATIONS + ); } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Type.java b/test/hotspot/jtreg/compiler/lib/template_library/Type.java index 6ad0ad2efb7f8..2f8c5c0b428ca 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Type.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Type.java @@ -107,6 +107,11 @@ public abstract class Type implements Name.Type { VECTOR_API_DOUBLE_TYPES ).flatMap((List l) -> l.stream()).toList(); + public static final List ALL_BUILTIN_TYPES = Library.concat( + PRIMITIVE_TYPES, + VECTOR_API_TYPES + ); + /** * Returns name of the type that can be used in Java code. * diff --git a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java index ac013ba29d558..addbe580f35ae 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java @@ -85,8 +85,9 @@ public boolean isSubtypeOf(Name.Type other) { @Override public final Object con() { - int r = RANDOM.nextInt(32); + int r = RANDOM.nextInt(64); if (r == 0) { return "null"; } + if (r == 1) { return vectorType + ".zero(" + species + ")"; } return List.of(vectorType, ".broadcast(", species, ", ", elementType.con(), ")"); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java index 21128e35290f2..fdfcba9b43bc8 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java @@ -90,7 +90,7 @@ public static String generate(CompileFramework comp) { // hook are both already set before we call "expression.withRandomArgs", and so that // we know we can generate fields and local variables. var template1Body = Template.make("type", (Type type)-> { - Expression expression = Expression.make(type, Type.VECTOR_API_TYPES, 4); + Expression expression = Expression.make(type, Type.ALL_BUILTIN_TYPES, 4); return body( """ try { From 8a292856a6d1362c0a293d1fb40dc1ac513c8155 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 3 Mar 2025 09:29:00 +0100 Subject: [PATCH 134/212] fix broadcast operation --- .../hotspot/jtreg/compiler/lib/template_library/Operations.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 1cad1079ab48a..58657d0df476d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -314,7 +314,7 @@ private static final List generateVectorAPIOperations() { List ops = new ArrayList(); for (var type : Type.VECTOR_API_TYPES) { - ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(", type.elementType, ")")); + ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", type.elementType, ")")); } // Ensure the list is immutable. From 2e699fe503bd358c91e9e56cc6e1d08228119190 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 3 Mar 2025 13:21:44 +0100 Subject: [PATCH 135/212] a few more ops and fix null for vector api --- .../lib/template_library/Operations.java | 15 +++++++++++ .../lib/template_library/PrimitiveType.java | 25 +++++++++++++++++++ .../lib/template_library/VectorAPIType.java | 3 +-- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 58657d0df476d..b7cda3f294710 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -315,6 +315,21 @@ private static final List generateVectorAPIOperations() { for (var type : Type.VECTOR_API_TYPES) { ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", type.elementType, ")")); + ops.add(new Operation.Unary(type, "", type, ".abs()")); + ops.add(new Operation.Binary(type, "", type, ".add(", type.elementType, ")")); + // TODO: add(int e, VectorMask m) + ops.add(new Operation.Binary(type, "", type, ".add(", type, ")")); + // TODO: add(Vector v, VectorMask m) + ops.add(new Operation.Binary(type, "", type, ".addIndex(", Type.ints(), ")")); + + if (!type.elementType.isFloating()) { + ops.add(new Operation.Binary(type, "", type, ".and(", type.elementType, ")")); + ops.add(new Operation.Binary(type, "", type, ".and(", type, ")")); + ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type.elementType, ", ", type.elementType, ")")); + ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type.elementType, ", ", type, ")")); + ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type, ", ", type.elementType, ")")); + ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type, ", ", type, ")")); + } } // Ensure the list is immutable. diff --git a/test/hotspot/jtreg/compiler/lib/template_library/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_library/PrimitiveType.java index 2090e26a4efe7..a178b5aa38abe 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/PrimitiveType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/PrimitiveType.java @@ -51,6 +51,7 @@ public boolean isSubtypeOf(Name.Type other) { public abstract int sizeInBits(); public abstract String boxedTypeName(); public abstract String vectorAPITypeName(); + public abstract boolean isFloating(); static final class ByteType extends PrimitiveType { public static final ByteType INSTANCE = new ByteType(); @@ -71,6 +72,9 @@ public final Object con() { @Override public int sizeInBits() { return 8; } + + @Override + public final boolean isFloating() { return false; } } static final class CharType extends PrimitiveType { @@ -92,6 +96,9 @@ public final Object con() { @Override public int sizeInBits() { return 16; } + + @Override + public final boolean isFloating() { return false; } } static final class ShortType extends PrimitiveType { @@ -113,6 +120,9 @@ public final Object con() { @Override public int sizeInBits() { return 16; } + + @Override + public final boolean isFloating() { return false; } } static final class IntType extends PrimitiveType { @@ -134,6 +144,9 @@ public final Object con() { @Override public int sizeInBits() { return 32; } + + @Override + public final boolean isFloating() { return false; } } static final class LongType extends PrimitiveType { @@ -155,6 +168,9 @@ public final Object con() { @Override public int sizeInBits() { return 64; } + + @Override + public final boolean isFloating() { return false; } } static final class FloatType extends PrimitiveType { @@ -176,6 +192,9 @@ public final Object con() { @Override public int sizeInBits() { return 32; } + + @Override + public final boolean isFloating() { return true; } } static final class DoubleType extends PrimitiveType { @@ -197,6 +216,9 @@ public final Object con() { @Override public int sizeInBits() { return 64; } + + @Override + public final boolean isFloating() { return true; } } static final class BooleanType extends PrimitiveType { @@ -219,5 +241,8 @@ public final Object con() { @Override public int sizeInBits() { throw new UnsupportedOperationException("boolean does not have number of bits"); } + + @Override + public final boolean isFloating() { return false; } } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java index addbe580f35ae..967066b13f35f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java @@ -86,8 +86,7 @@ public boolean isSubtypeOf(Name.Type other) { @Override public final Object con() { int r = RANDOM.nextInt(64); - if (r == 0) { return "null"; } - if (r == 1) { return vectorType + ".zero(" + species + ")"; } + if (r == 0) { return vectorType + ".zero(" + species + ")"; } return List.of(vectorType, ".broadcast(", species, ", ", elementType.con(), ")"); } } From 627a0485387ad829ee50b7317d2186b3855af701 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 3 Mar 2025 13:47:58 +0100 Subject: [PATCH 136/212] more ops --- .../jtreg/compiler/lib/template_library/Operations.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index b7cda3f294710..ca06ae93a0e89 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -314,7 +314,6 @@ private static final List generateVectorAPIOperations() { List ops = new ArrayList(); for (var type : Type.VECTOR_API_TYPES) { - ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", type.elementType, ")")); ops.add(new Operation.Unary(type, "", type, ".abs()")); ops.add(new Operation.Binary(type, "", type, ".add(", type.elementType, ")")); // TODO: add(int e, VectorMask m) @@ -330,6 +329,13 @@ private static final List generateVectorAPIOperations() { ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type, ", ", type.elementType, ")")); ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type, ", ", type, ")")); } + + // TODO: blend(int e, VectorMask m) + // TODO: blend(long e, VectorMask m) + // TODO: blend(Vector v, VectorMask m) + + ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", type.elementType, ")")); + ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", Type.longs(), ")")); } // Ensure the list is immutable. From 172d292065807d635d21e621d90bffa33bd72362 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 3 Mar 2025 13:54:11 +0100 Subject: [PATCH 137/212] make methods final --- .../lib/template_library/PrimitiveType.java | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_library/PrimitiveType.java index a178b5aa38abe..a20c5c0eeeb4d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/PrimitiveType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/PrimitiveType.java @@ -60,10 +60,10 @@ static final class ByteType extends PrimitiveType { public final String name() { return "byte"; } @Override - public String boxedTypeName() { return "Byte"; } + public final String boxedTypeName() { return "Byte"; } @Override - public String vectorAPITypeName() { return "ByteVector"; } + public final String vectorAPITypeName() { return "ByteVector"; } @Override public final Object con() { @@ -71,7 +71,7 @@ public final Object con() { } @Override - public int sizeInBits() { return 8; } + public final int sizeInBits() { return 8; } @Override public final boolean isFloating() { return false; } @@ -84,10 +84,10 @@ static final class CharType extends PrimitiveType { public final String name() { return "char"; } @Override - public String boxedTypeName() { return "Character"; } + public final String boxedTypeName() { return "Character"; } @Override - public String vectorAPITypeName() { throw new UnsupportedOperationException("VectorAPI has no char vector type."); } + public final String vectorAPITypeName() { throw new UnsupportedOperationException("VectorAPI has no char vector type."); } @Override public final Object con() { @@ -95,7 +95,7 @@ public final Object con() { } @Override - public int sizeInBits() { return 16; } + public final int sizeInBits() { return 16; } @Override public final boolean isFloating() { return false; } @@ -108,10 +108,10 @@ static final class ShortType extends PrimitiveType { public final String name() { return "short"; } @Override - public String boxedTypeName() { return "Short"; } + public final String boxedTypeName() { return "Short"; } @Override - public String vectorAPITypeName() { return "ShortVector"; } + public final String vectorAPITypeName() { return "ShortVector"; } @Override public final Object con() { @@ -119,7 +119,7 @@ public final Object con() { } @Override - public int sizeInBits() { return 16; } + public final int sizeInBits() { return 16; } @Override public final boolean isFloating() { return false; } @@ -132,10 +132,10 @@ static final class IntType extends PrimitiveType { public final String name() { return "int"; } @Override - public String boxedTypeName() { return "Integer"; } + public final String boxedTypeName() { return "Integer"; } @Override - public String vectorAPITypeName() { return "IntVector"; } + public final String vectorAPITypeName() { return "IntVector"; } @Override public final Object con() { @@ -143,7 +143,7 @@ public final Object con() { } @Override - public int sizeInBits() { return 32; } + public final int sizeInBits() { return 32; } @Override public final boolean isFloating() { return false; } @@ -156,10 +156,10 @@ static final class LongType extends PrimitiveType { public final String name() { return "long"; } @Override - public String boxedTypeName() { return "Long"; } + public final String boxedTypeName() { return "Long"; } @Override - public String vectorAPITypeName() { return "LongVector"; } + public final String vectorAPITypeName() { return "LongVector"; } @Override public final Object con() { @@ -167,7 +167,7 @@ public final Object con() { } @Override - public int sizeInBits() { return 64; } + public final int sizeInBits() { return 64; } @Override public final boolean isFloating() { return false; } @@ -180,10 +180,10 @@ static final class FloatType extends PrimitiveType { public final String name() { return "float"; } @Override - public String boxedTypeName() { return "Float"; } + public final String boxedTypeName() { return "Float"; } @Override - public String vectorAPITypeName() { return "FloatVector"; } + public final String vectorAPITypeName() { return "FloatVector"; } @Override public final Object con() { @@ -191,7 +191,7 @@ public final Object con() { } @Override - public int sizeInBits() { return 32; } + public final int sizeInBits() { return 32; } @Override public final boolean isFloating() { return true; } @@ -204,10 +204,10 @@ static final class DoubleType extends PrimitiveType { public final String name() { return "double"; } @Override - public String boxedTypeName() { return "Double"; } + public final String boxedTypeName() { return "Double"; } @Override - public String vectorAPITypeName() { return "DoubleVector"; } + public final String vectorAPITypeName() { return "DoubleVector"; } @Override public final Object con() { @@ -215,7 +215,7 @@ public final Object con() { } @Override - public int sizeInBits() { return 64; } + public final int sizeInBits() { return 64; } @Override public final boolean isFloating() { return true; } @@ -228,10 +228,10 @@ static final class BooleanType extends PrimitiveType { public final String name() { return "boolean"; } @Override - public String boxedTypeName() { return "Boolean"; } + public final String boxedTypeName() { return "Boolean"; } @Override - public String vectorAPITypeName() { throw new UnsupportedOperationException("VectorAPI has no boolean vector type."); } + public final String vectorAPITypeName() { throw new UnsupportedOperationException("VectorAPI has no boolean vector type."); } @Override public final Object con() { @@ -240,7 +240,7 @@ public final Object con() { } @Override - public int sizeInBits() { throw new UnsupportedOperationException("boolean does not have number of bits"); } + public final int sizeInBits() { throw new UnsupportedOperationException("boolean does not have number of bits"); } @Override public final boolean isFloating() { return false; } From c0d6c4a4c9855bede5eb2a5ff84d299883ea3907 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 3 Mar 2025 15:22:48 +0100 Subject: [PATCH 138/212] load from / store to array --- .../examples/TestFuzzVectorAPI.java | 71 ++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java index fdfcba9b43bc8..8703a72e97af6 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java @@ -48,6 +48,8 @@ import compiler.lib.template_library.Library; import compiler.lib.template_library.IRTestClass; import compiler.lib.template_library.Type; +import compiler.lib.template_library.PrimitiveType; +import compiler.lib.template_library.VectorAPIType; import compiler.lib.template_library.Expression; import compiler.lib.template_library.Value; @@ -141,11 +143,76 @@ public static String generate(CompileFramework comp) { """ )); + // Example 2: + // We use the expression to iterate over arrays, loading from a set of input arrays, + // and storing to an output array. + var template2 = Template.make("type", (VectorAPIType type)-> { + int size = 1000; + Expression expression = Expression.make(type, Type.ALL_BUILTIN_TYPES, 4); + List types = expression.types(); + List arrayDefinitions = new ArrayList<>(); + List args = new ArrayList<>(); + for (int i = 0; i < types.size(); i++) { + String name = $("array") + "_" + i; + Type argType = types.get(i); + PrimitiveType elementType = null; + if (argType instanceof PrimitiveType t) { + elementType = t; + args.add(name + "[0]"); + } else if (argType instanceof VectorAPIType vt) { + elementType = vt.elementType; + args.add(vt.vectorType + ".fromArray(" + vt.species + ", " + name + ", 0)"); + } + arrayDefinitions.add(defineArray.withArgs(elementType, name, size)); + } + return body( + let("size", size), + let("elementType", type.elementType), + """ + // --- $test start --- + // Using $GOLD + // type: #type + // elementType: #elementType + """, + Library.CLASS_HOOK.set( + """ + // Input arrays: + """, + arrayDefinitions, + """ + + static final Object $GOLD = $test(); + + @Test + public static Object $test() { + try { + #elementType[] out = new #elementType[#size]; + """, + " ", expression.withArgs(args), ".intoArray(out, 0);\n", + """ + return out; + } catch (Exception e) { + return e; + } + } + + @Check(test = "$test") + public static void $check(Object result) { + Verify.checkEQ(result, $GOLD); + } + + // --- $test end --- + """ + ) + ); + }); + // Now use the templates and add them into the IRTestClass. List templates = new ArrayList<>(); templates.add(Library.arrayFillMethods()); - for (Type type : Type.VECTOR_API_TYPES) { - for (int i = 0; i < 20; i++) { templates.add(template1.withArgs(type)); } + for (VectorAPIType type : Type.VECTOR_API_TYPES) { + for (int i = 0; i < 10; i++) { templates.add(template1.withArgs(type)); } + for (int i = 0; i < 5; i++) { templates.add(template2.withArgs(type)); } } return IRTestClass.TEMPLATE.withArgs(info, templates).render(); } From 05d0e93ac3c8f58586d1f5acc43499293cb9d180 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 3 Mar 2025 16:18:07 +0100 Subject: [PATCH 139/212] add castShape --- .../jtreg/compiler/lib/template_library/Operations.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index ca06ae93a0e89..4a03b70e26d45 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -336,6 +336,11 @@ private static final List generateVectorAPIOperations() { ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", type.elementType, ")")); ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", Type.longs(), ")")); + + // TODO: non zero parts + for (var type2 : Type.VECTOR_API_TYPES) { + ops.add(new Operation.Unary(type, "((" + type.vectorType + ")", type2 , ".castShape(" + type.species + ", 0))")); + } } // Ensure the list is immutable. From ecf22e7c7daae3d94dd063afee515b03ad969ed8 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 4 Mar 2025 15:23:11 +0100 Subject: [PATCH 140/212] convert vector api --- .../lib/template_library/Operations.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 4a03b70e26d45..b0bdfa9ee5695 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -341,6 +341,27 @@ private static final List generateVectorAPIOperations() { for (var type2 : Type.VECTOR_API_TYPES) { ops.add(new Operation.Unary(type, "((" + type.vectorType + ")", type2 , ".castShape(" + type.species + ", 0))")); } + + // Note: check works on class / species, leaving them out. + + // TODO: compare with VectorMask type + // TODO: compress with VectorMask type + + // TODO: non zero parts + for (var type2 : Type.VECTOR_API_TYPES) { + ops.add(new Operation.Unary(type, + "((" + type.vectorType + ")", + type2 , + ".convert(VectorOperators.Conversion.ofCast(" + + type2.elementType.boxedTypeName() + ".class, " + + type.elementType.boxedTypeName() + ".class), 0))")); + ops.add(new Operation.Unary(type, + "((" + type.vectorType + ")", + type2 , + ".convert(VectorOperators.Conversion.ofReinterpret(" + + type2.elementType.boxedTypeName() + ".class, " + + type.elementType.boxedTypeName() + ".class), 0))")); + } } // Ensure the list is immutable. From e3facb2d38f38501d10d098527c3d886c8ddffdd Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 4 Mar 2025 15:34:06 +0100 Subject: [PATCH 141/212] more vector ops --- .../lib/template_library/Operations.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index b0bdfa9ee5695..99695081d18aa 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -361,7 +361,23 @@ private static final List generateVectorAPIOperations() { ".convert(VectorOperators.Conversion.ofReinterpret(" + type2.elementType.boxedTypeName() + ".class, " + type.elementType.boxedTypeName() + ".class), 0))")); + + // TODO: convertShape } + + ops.add(new Operation.Binary(type, "", type, ".div(", type.elementType, ")")); + // TODO: div(int e, VectorMask m) + ops.add(new Operation.Binary(type, "", type, ".div(", type, ")")); + // TODO: div(Vector v, VectorMask m) + + // TODO: eq(int e) -> VectorMask + // TODO: eq(Vector v) -> VectorMask + + // TODO: expand(VectorMask m) + + // TODO: ensure we use all variants of fromArray and fromMemorySegment, plus intoArray and intoMemorySegment. + + ops.add(new Operation.Binary(type.elementType, "", type, ".lane(", Type.ints(), ")")); } // Ensure the list is immutable. From d1e68fa488c3de52ef7e1a963c67118d61ccdd62 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 4 Mar 2025 16:46:09 +0100 Subject: [PATCH 142/212] wip lanewise for vector api --- .../lib/template_library/Operations.java | 67 +++++++++++++++++++ .../compiler/lib/template_library/Type.java | 23 +++++++ 2 files changed, 90 insertions(+) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 99695081d18aa..7e5d49f272c7a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -310,6 +310,66 @@ final class Operations { BOOLEAN_OPERATIONS ).flatMap((List l) -> l.stream()).toList(); + + private record VOP(String name, int args, List elementTypes) {} + + private static final List VECTOR_API_OPS = List.of( + new VOP("ABS", 1, Type.PRIMITIVE_TYPES), + new VOP("ACOS", 1, Type.FLOATING_TYPES), + new VOP("ADD", 2, Type.PRIMITIVE_TYPES), + new VOP("AND", 2, Type.INTEGRAL_TYPES), + new VOP("AND_NOT", 2, Type.INTEGRAL_TYPES), + new VOP("ASHR", 2, Type.INTEGRAL_TYPES), + new VOP("ASIN", 1, Type.FLOATING_TYPES), + new VOP("ATAN", 1, Type.FLOATING_TYPES), + new VOP("ATAN2", 2, Type.FLOATING_TYPES), + new VOP("BIT_COUNT", 1, Type.INTEGRAL_TYPES), + new VOP("BITWISE_BLEND", 3, Type.INTEGRAL_TYPES), + new VOP("CBRT", 1, Type.FLOATING_TYPES), + new VOP("COMPRESS_BITS", 2, Type.INT_LONG_TYPES), + new VOP("COS", 1, Type.FLOATING_TYPES), + new VOP("COSH", 1, Type.FLOATING_TYPES), + new VOP("DIV", 2, Type.FLOATING_TYPES), + new VOP("EXP", 1, Type.FLOATING_TYPES), + new VOP("EXPAND_BITS", 2, Type.INT_LONG_TYPES), + new VOP("EXPM1", 1, Type.FLOATING_TYPES), + new VOP("FIRST_NONZERO", 1, Type.PRIMITIVE_TYPES), + new VOP("FMA", 3, Type.FLOATING_TYPES), + new VOP("HYPOT", 2, Type.FLOATING_TYPES), + new VOP("LEADING_ZEROS_COUNT", 1, Type.PRIMITIVE_TYPES), + new VOP("LOG", 1, Type.FLOATING_TYPES), + new VOP("LOG10", 1, Type.FLOATING_TYPES), + new VOP("LOG1P", 1, Type.FLOATING_TYPES), + new VOP("LSHL", 2, Type.INTEGRAL_TYPES), + new VOP("LSHR", 2, Type.INTEGRAL_TYPES), + new VOP("MIN", 2, Type.PRIMITIVE_TYPES), + new VOP("MAX", 2, Type.PRIMITIVE_TYPES), + new VOP("MUL", 2, Type.PRIMITIVE_TYPES), + new VOP("NEG", 1, Type.PRIMITIVE_TYPES), + new VOP("NOT", 1, Type.INTEGRAL_TYPES), + new VOP("OR", 2, Type.INTEGRAL_TYPES), + new VOP("POW", 2, Type.FLOATING_TYPES), + new VOP("REVERSE", 1, Type.PRIMITIVE_TYPES), + new VOP("REVERSE_BYTES", 1, Type.PRIMITIVE_TYPES), + new VOP("ROL", 2, Type.INTEGRAL_TYPES), + new VOP("ROR", 2, Type.INTEGRAL_TYPES), + new VOP("SADD", 2, Type.INTEGRAL_TYPES), + new VOP("SIN", 1, Type.FLOATING_TYPES), + new VOP("SINH", 1, Type.FLOATING_TYPES), + new VOP("SQRT", 1, Type.FLOATING_TYPES), + new VOP("SSUB", 2, Type.INTEGRAL_TYPES), + new VOP("SUADD", 2, Type.INTEGRAL_TYPES), + new VOP("SUB", 2, Type.PRIMITIVE_TYPES), + new VOP("SUSUB", 2, Type.INTEGRAL_TYPES), + new VOP("TAN", 1, Type.FLOATING_TYPES), + new VOP("TANH", 1, Type.FLOATING_TYPES), + new VOP("TRAILING_ZEROS_COUNT", 1, Type.PRIMITIVE_TYPES), + new VOP("UMAX", 2, Type.INTEGRAL_TYPES), + new VOP("UMIN", 2, Type.INTEGRAL_TYPES), + new VOP("XOR", 2, Type.INTEGRAL_TYPES), + new VOP("ZOMO", 1, Type.INTEGRAL_TYPES) + ); + private static final List generateVectorAPIOperations() { List ops = new ArrayList(); @@ -378,6 +438,13 @@ private static final List generateVectorAPIOperations() { // TODO: ensure we use all variants of fromArray and fromMemorySegment, plus intoArray and intoMemorySegment. ops.add(new Operation.Binary(type.elementType, "", type, ".lane(", Type.ints(), ")")); + + for (VOP vop : VECTOR_API_OPS) { + if (vop.args() == 2 && vop.elementTypes().contains(type.elementType)) { + ops.add(new Operation.Binary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ")")); + // TODO: lanewise(VectorOperators.Binary op, int e, VectorMask m) + } + } } // Ensure the list is immutable. diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Type.java b/test/hotspot/jtreg/compiler/lib/template_library/Type.java index 2f8c5c0b428ca..9941e12c8b31d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Type.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Type.java @@ -56,6 +56,29 @@ public abstract class Type implements Name.Type { booleans() ); + public static final List INTEGRAL_TYPES = List.of( + bytes(), + chars(), + shorts(), + ints(), + longs() + ); + public static final List SUBWORD_TYPES = List.of( + bytes(), + chars(), + shorts() + ); + + public static final List INT_LONG_TYPES = List.of( + ints(), + longs() + ); + + public static final List FLOATING_TYPES = List.of( + floats(), + doubles() + ); + public static final List VECTOR_API_BYTE_TYPES = List.of( VectorAPIType.BYTE_64, VectorAPIType.BYTE_128, From 1f6fb6b9d6243ee7a355eecd78c5d41e56884aca Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 4 Mar 2025 18:35:51 +0100 Subject: [PATCH 143/212] add list of exceptions to operations --- .../lib/template_library/Expression.java | 6 +- .../lib/template_library/Operation.java | 8 +- .../lib/template_library/Operations.java | 392 +++++++++--------- 3 files changed, 204 insertions(+), 202 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index 4e3415d38d30a..5800ba75a0726 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -127,7 +127,7 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result } } switch (Library.choice(resultTypeOps)) { - case Operation.Unary(Type r, String s0, Type t0, String s1) -> { + case Operation.Unary(Type r, String s0, Type t0, String s1, List> exceptions) -> { ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, types); return (List tokens, List args) -> { tokens.add(s0); @@ -135,7 +135,7 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result tokens.add(s1); }; } - case Operation.Binary(Type r, String s0, Type t0, String s1, Type t1, String s2) -> { + case Operation.Binary(Type r, String s0, Type t0, String s1, Type t1, String s2, List> exceptions) -> { ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, types); ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, ops, maxDepth-1, types); return (List tokens, List args) -> { @@ -146,7 +146,7 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result tokens.add(s2); }; } - case Operation.Ternary(Type r, String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3) -> { + case Operation.Ternary(Type r, String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3, List> exceptions) -> { ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, types); ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, ops, maxDepth-1, types); ExpressionGeneratorStep step2 = expressionGeneratorStep(t1, ops, maxDepth-1, types); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operation.java b/test/hotspot/jtreg/compiler/lib/template_library/Operation.java index 09ca4f2a8cb48..eea5b3f00b98b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operation.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operation.java @@ -23,6 +23,7 @@ package compiler.lib.template_library; +import java.util.List; import java.util.HashSet; public sealed interface Operation permits Operation.Unary, @@ -37,13 +38,12 @@ public sealed interface Operation permits Operation.Unary, public abstract boolean matchesTypes(HashSet types); - public static record Unary(Type r, String s0, Type t0, String s1) implements Operation { + public static record Unary(Type r, String s0, Type t0, String s1, List> exceptions) implements Operation { @Override public boolean matchesReturnType(Type returnType) { return r.isSubtypeOf(returnType); } - @Override public boolean matchesTypes(HashSet types) { return types.contains(r) && @@ -51,7 +51,7 @@ public boolean matchesTypes(HashSet types) { } } - public static record Binary(Type r, String s0, Type t0, String s1, Type t1, String s2) implements Operation { + public static record Binary(Type r, String s0, Type t0, String s1, Type t1, String s2, List> exceptions) implements Operation { @Override public boolean matchesReturnType(Type returnType) { return r.isSubtypeOf(returnType); @@ -65,7 +65,7 @@ public boolean matchesTypes(HashSet types) { } } - public static record Ternary(Type r, String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3) implements Operation { + public static record Ternary(Type r, String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3, List> exceptions) implements Operation { @Override public boolean matchesReturnType(Type returnType) { return r.isSubtypeOf(returnType); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 7e5d49f272c7a..08a80446fe2ce 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -38,15 +38,15 @@ final class Operations { // Instead of adding these operations explicitly, we just add the conversion // from int to byte, and let the IntType generate all the integer arithmetic // operations. - new Operation.Unary(Type.bytes(), "((byte)", Type.chars(), ")"), - new Operation.Unary(Type.bytes(), "((byte)", Type.shorts(), ")"), - new Operation.Unary(Type.bytes(), "((byte)", Type.ints(), ")"), - new Operation.Unary(Type.bytes(), "((byte)", Type.longs(), ")"), - new Operation.Unary(Type.bytes(), "((byte)", Type.floats(), ")"), - new Operation.Unary(Type.bytes(), "((byte)", Type.doubles(), ")"), + new Operation.Unary(Type.bytes(), "((byte)", Type.chars(), ")", null), + new Operation.Unary(Type.bytes(), "((byte)", Type.shorts(), ")", null), + new Operation.Unary(Type.bytes(), "((byte)", Type.ints(), ")", null), + new Operation.Unary(Type.bytes(), "((byte)", Type.longs(), ")", null), + new Operation.Unary(Type.bytes(), "((byte)", Type.floats(), ")", null), + new Operation.Unary(Type.bytes(), "((byte)", Type.doubles(), ")", null), // Note: There is no cast from boolean - new Operation.Ternary(Type.bytes(), "(", Type.booleans(), " ? ", Type.bytes(), " : ", Type.bytes(), ")") + new Operation.Ternary(Type.bytes(), "(", Type.booleans(), " ? ", Type.bytes(), " : ", Type.bytes(), ")", null) ); private static final List CHAR_OPERATIONS = List.of( @@ -58,17 +58,17 @@ final class Operations { // Instead of adding these operations explicitly, we just add the conversion // from int to char, and let the IntType generate all the integer arithmetic // operations. - new Operation.Unary(Type.chars(), "((char)", Type.bytes(), ")"), - new Operation.Unary(Type.chars(), "((char)", Type.shorts(), ")"), - new Operation.Unary(Type.chars(), "((char)", Type.ints(), ")"), - new Operation.Unary(Type.chars(), "((char)", Type.longs(), ")"), - new Operation.Unary(Type.chars(), "((char)", Type.floats(), ")"), - new Operation.Unary(Type.chars(), "((char)", Type.doubles(), ")"), + new Operation.Unary(Type.chars(), "((char)", Type.bytes(), ")", null), + new Operation.Unary(Type.chars(), "((char)", Type.shorts(), ")", null), + new Operation.Unary(Type.chars(), "((char)", Type.ints(), ")", null), + new Operation.Unary(Type.chars(), "((char)", Type.longs(), ")", null), + new Operation.Unary(Type.chars(), "((char)", Type.floats(), ")", null), + new Operation.Unary(Type.chars(), "((char)", Type.doubles(), ")", null), // Note: There is no cast from boolean - new Operation.Unary(Type.chars(), "Character.reverseBytes(", Type.chars(), ")"), + new Operation.Unary(Type.chars(), "Character.reverseBytes(", Type.chars(), ")", null), - new Operation.Ternary(Type.chars(), "(", Type.booleans(), " ? ", Type.chars(), " : ", Type.chars(), ")") + new Operation.Ternary(Type.chars(), "(", Type.booleans(), " ? ", Type.chars(), " : ", Type.chars(), ")", null) ); private static final List SHORT_OPERATIONS = List.of( @@ -80,223 +80,223 @@ final class Operations { // Instead of adding these operations explicitly, we just add the conversion // from int to short, and let the IntType generate all the integer arithmetic // operations. - new Operation.Unary(Type.shorts(), "((short)", Type.bytes(), ")"), - new Operation.Unary(Type.shorts(), "((short)", Type.chars(), ")"), - new Operation.Unary(Type.shorts(), "((short)", Type.ints(), ")"), - new Operation.Unary(Type.shorts(), "((short)", Type.longs(), ")"), - new Operation.Unary(Type.shorts(), "((short)", Type.floats(), ")"), - new Operation.Unary(Type.shorts(), "((short)", Type.doubles(), ")"), + new Operation.Unary(Type.shorts(), "((short)", Type.bytes(), ")", null), + new Operation.Unary(Type.shorts(), "((short)", Type.chars(), ")", null), + new Operation.Unary(Type.shorts(), "((short)", Type.ints(), ")", null), + new Operation.Unary(Type.shorts(), "((short)", Type.longs(), ")", null), + new Operation.Unary(Type.shorts(), "((short)", Type.floats(), ")", null), + new Operation.Unary(Type.shorts(), "((short)", Type.doubles(), ")", null), // Note: There is no cast from boolean - new Operation.Unary(Type.shorts(), "Short.reverseBytes(", Type.shorts(), ")"), + new Operation.Unary(Type.shorts(), "Short.reverseBytes(", Type.shorts(), ")", null), // Note: Float.floatToFloat16 can lead to issues, because NaN values are not always // represented by the same short bits. - new Operation.Ternary(Type.shorts(), "(", Type.booleans(), " ? ", Type.shorts(), " : ", Type.shorts(), ")") + new Operation.Ternary(Type.shorts(), "(", Type.booleans(), " ? ", Type.shorts(), " : ", Type.shorts(), ")", null) ); private static final List INT_OPERATIONS = List.of( - new Operation.Unary(Type.ints(), "((int)", Type.bytes(), ")"), - new Operation.Unary(Type.ints(), "((int)", Type.chars(), ")"), - new Operation.Unary(Type.ints(), "((int)", Type.shorts(), ")"), - new Operation.Unary(Type.ints(), "((int)", Type.longs(), ")"), - new Operation.Unary(Type.ints(), "((int)", Type.floats(), ")"), - new Operation.Unary(Type.ints(), "((int)", Type.doubles(), ")"), + new Operation.Unary(Type.ints(), "((int)", Type.bytes(), ")", null), + new Operation.Unary(Type.ints(), "((int)", Type.chars(), ")", null), + new Operation.Unary(Type.ints(), "((int)", Type.shorts(), ")", null), + new Operation.Unary(Type.ints(), "((int)", Type.longs(), ")", null), + new Operation.Unary(Type.ints(), "((int)", Type.floats(), ")", null), + new Operation.Unary(Type.ints(), "((int)", Type.doubles(), ")", null), // Note: There is no cast from boolean - new Operation.Unary(Type.ints(), "(-(", Type.ints(), "))"), - new Operation.Unary(Type.ints(), "(~", Type.ints(), ")"), - - new Operation.Binary(Type.ints(), "(", Type.ints(), " + ", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "(", Type.ints(), " - ", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "(", Type.ints(), " * ", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "(", Type.ints(), " / ", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "(", Type.ints(), " % ", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "(", Type.ints(), " & ", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "(", Type.ints(), " | ", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "(", Type.ints(), " ^ ", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "(", Type.ints(), " << ", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "(", Type.ints(), " >> ", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "(", Type.ints(), " >>> ", Type.ints(), ")"), - - new Operation.Binary(Type.ints(), "Byte.compare(", Type.bytes(), ", ", Type.bytes(), ")"), - new Operation.Binary(Type.ints(), "Byte.compareUnsigned(", Type.bytes(), ", ", Type.bytes(), ")"), - new Operation.Unary(Type.ints(), "Byte.toUnsignedInt(", Type.bytes(), ")"), - - new Operation.Binary(Type.ints(), "Character.compare(", Type.chars(), ", ", Type.chars(), ")"), - - new Operation.Binary(Type.ints(), "Short.compare(", Type.shorts(), ", ", Type.shorts(), ")"), - new Operation.Binary(Type.ints(), "Short.compareUnsigned(", Type.shorts(), ", ", Type.shorts(), ")"), - new Operation.Unary(Type.ints(), "Short.toUnsignedInt(", Type.shorts(), ")"), - - new Operation.Unary(Type.ints(), "Integer.bitCount(", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "Integer.compare(", Type.ints(), ", ", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "Integer.compareUnsigned(", Type.ints(), ", ", Type.ints(), ")"), - //new Operation.Binary(Type.ints(), "Integer.compress(", Type.ints(), ", ", Type.ints(), ")"), + new Operation.Unary(Type.ints(), "(-(", Type.ints(), "))", null), + new Operation.Unary(Type.ints(), "(~", Type.ints(), ")", null), + + new Operation.Binary(Type.ints(), "(", Type.ints(), " + ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "(", Type.ints(), " - ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "(", Type.ints(), " * ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "(", Type.ints(), " / ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "(", Type.ints(), " % ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "(", Type.ints(), " & ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "(", Type.ints(), " | ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "(", Type.ints(), " ^ ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "(", Type.ints(), " << ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "(", Type.ints(), " >> ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "(", Type.ints(), " >>> ", Type.ints(), ")", null), + + new Operation.Binary(Type.ints(), "Byte.compare(", Type.bytes(), ", ", Type.bytes(), ")", null), + new Operation.Binary(Type.ints(), "Byte.compareUnsigned(", Type.bytes(), ", ", Type.bytes(), ")", null), + new Operation.Unary(Type.ints(), "Byte.toUnsignedInt(", Type.bytes(), ")", null), + + new Operation.Binary(Type.ints(), "Character.compare(", Type.chars(), ", ", Type.chars(), ")", null), + + new Operation.Binary(Type.ints(), "Short.compare(", Type.shorts(), ", ", Type.shorts(), ")", null), + new Operation.Binary(Type.ints(), "Short.compareUnsigned(", Type.shorts(), ", ", Type.shorts(), ")", null), + new Operation.Unary(Type.ints(), "Short.toUnsignedInt(", Type.shorts(), ")", null), + + new Operation.Unary(Type.ints(), "Integer.bitCount(", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "Integer.compare(", Type.ints(), ", ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "Integer.compareUnsigned(", Type.ints(), ", ", Type.ints(), ")", null), + //new Operation.Binary(Type.ints(), "Integer.compress(", Type.ints(), ", ", Type.ints(), ")", null), // TODO: add back after JDK-8350896 - new Operation.Binary(Type.ints(), "Integer.divideUnsigned(", Type.ints(), ", ", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "Integer.expand(", Type.ints(), ", ", Type.ints(), ")"), - new Operation.Unary(Type.ints(), "Integer.highestOneBit(", Type.ints(), ")"), - new Operation.Unary(Type.ints(), "Integer.lowestOneBit(", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "Integer.min(", Type.ints(), ", ", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "Integer.max(", Type.ints(), ", ", Type.ints(), ")"), - new Operation.Unary(Type.ints(), "Integer.numberOfLeadingZeros(", Type.ints(), ")"), - new Operation.Unary(Type.ints(), "Integer.numberOfTrailingZeros(", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "Integer.remainderUnsigned(", Type.ints(), ", ", Type.ints(), ")"), - new Operation.Unary(Type.ints(), "Integer.reverse(", Type.ints(), ")"), - new Operation.Unary(Type.ints(), "Integer.reverseBytes(", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "Integer.rotateLeft(", Type.ints(), ", ", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "Integer.rotateRight(", Type.ints(), ", ", Type.ints(), ")"), - new Operation.Unary(Type.ints(), "Integer.signum(", Type.ints(), ")"), - new Operation.Binary(Type.ints(), "Integer.sum(", Type.ints(), ", ", Type.ints(), ")"), - - new Operation.Unary(Type.ints(), "Long.bitCount(", Type.longs(), ")"), - new Operation.Binary(Type.ints(), "Long.compare(", Type.longs(), ", ", Type.longs(), ")"), - new Operation.Binary(Type.ints(), "Long.compareUnsigned(", Type.longs(), ", ", Type.longs(), ")"), - new Operation.Unary(Type.ints(), "Long.numberOfLeadingZeros(", Type.longs(), ")"), - new Operation.Unary(Type.ints(), "Long.numberOfTrailingZeros(", Type.longs(), ")"), - new Operation.Unary(Type.ints(), "Long.signum(", Type.longs(), ")"), - - new Operation.Binary(Type.ints(), "Float.compare(", Type.floats(), ", ", Type.floats(), ")"), - new Operation.Unary(Type.ints(), "Float.floatToIntBits(", Type.floats(), ")"), + new Operation.Binary(Type.ints(), "Integer.divideUnsigned(", Type.ints(), ", ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "Integer.expand(", Type.ints(), ", ", Type.ints(), ")", null), + new Operation.Unary(Type.ints(), "Integer.highestOneBit(", Type.ints(), ")", null), + new Operation.Unary(Type.ints(), "Integer.lowestOneBit(", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "Integer.min(", Type.ints(), ", ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "Integer.max(", Type.ints(), ", ", Type.ints(), ")", null), + new Operation.Unary(Type.ints(), "Integer.numberOfLeadingZeros(", Type.ints(), ")", null), + new Operation.Unary(Type.ints(), "Integer.numberOfTrailingZeros(", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "Integer.remainderUnsigned(", Type.ints(), ", ", Type.ints(), ")", null), + new Operation.Unary(Type.ints(), "Integer.reverse(", Type.ints(), ")", null), + new Operation.Unary(Type.ints(), "Integer.reverseBytes(", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "Integer.rotateLeft(", Type.ints(), ", ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "Integer.rotateRight(", Type.ints(), ", ", Type.ints(), ")", null), + new Operation.Unary(Type.ints(), "Integer.signum(", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "Integer.sum(", Type.ints(), ", ", Type.ints(), ")", null), + + new Operation.Unary(Type.ints(), "Long.bitCount(", Type.longs(), ")", null), + new Operation.Binary(Type.ints(), "Long.compare(", Type.longs(), ", ", Type.longs(), ")", null), + new Operation.Binary(Type.ints(), "Long.compareUnsigned(", Type.longs(), ", ", Type.longs(), ")", null), + new Operation.Unary(Type.ints(), "Long.numberOfLeadingZeros(", Type.longs(), ")", null), + new Operation.Unary(Type.ints(), "Long.numberOfTrailingZeros(", Type.longs(), ")", null), + new Operation.Unary(Type.ints(), "Long.signum(", Type.longs(), ")", null), + + new Operation.Binary(Type.ints(), "Float.compare(", Type.floats(), ", ", Type.floats(), ")", null), + new Operation.Unary(Type.ints(), "Float.floatToIntBits(", Type.floats(), ")", null), // Note: Float.floatToRawIntBits can lead to issues, because the NaN values are not always // represented by the same int bits. - new Operation.Binary(Type.ints(), "Double.compare(", Type.doubles(), ", ", Type.doubles(), ")"), + new Operation.Binary(Type.ints(), "Double.compare(", Type.doubles(), ", ", Type.doubles(), ")", null), - new Operation.Binary(Type.ints(), "Boolean.compare(", Type.booleans(), ", ", Type.booleans(), ")"), + new Operation.Binary(Type.ints(), "Boolean.compare(", Type.booleans(), ", ", Type.booleans(), ")", null), - new Operation.Ternary(Type.ints(), "(", Type.booleans(), " ? ", Type.ints(), " : ", Type.ints(), ")") + new Operation.Ternary(Type.ints(), "(", Type.booleans(), " ? ", Type.ints(), " : ", Type.ints(), ")", null) ); private static final List LONG_OPERATIONS = List.of( - new Operation.Unary(Type.longs(), "((long)", Type.bytes(), ")"), - new Operation.Unary(Type.longs(), "((long)", Type.chars(), ")"), - new Operation.Unary(Type.longs(), "((long)", Type.shorts(), ")"), - new Operation.Unary(Type.longs(), "((long)", Type.ints(), ")"), - new Operation.Unary(Type.longs(), "((long)", Type.floats(), ")"), - new Operation.Unary(Type.longs(), "((long)", Type.doubles(), ")"), + new Operation.Unary(Type.longs(), "((long)", Type.bytes(), ")", null), + new Operation.Unary(Type.longs(), "((long)", Type.chars(), ")", null), + new Operation.Unary(Type.longs(), "((long)", Type.shorts(), ")", null), + new Operation.Unary(Type.longs(), "((long)", Type.ints(), ")", null), + new Operation.Unary(Type.longs(), "((long)", Type.floats(), ")", null), + new Operation.Unary(Type.longs(), "((long)", Type.doubles(), ")", null), // Note: There is no cast from boolean - new Operation.Unary(Type.longs(), "(-(", Type.longs(), "))"), - new Operation.Unary(Type.longs(), "(~", Type.longs(), ")"), + new Operation.Unary(Type.longs(), "(-(", Type.longs(), "))", null), + new Operation.Unary(Type.longs(), "(~", Type.longs(), ")", null), - new Operation.Binary(Type.longs(), "(", Type.longs(), " + ", Type.longs(), ")"), - new Operation.Binary(Type.longs(), "(", Type.longs(), " - ", Type.longs(), ")"), - new Operation.Binary(Type.longs(), "(", Type.longs(), " * ", Type.longs(), ")"), - new Operation.Binary(Type.longs(), "(", Type.longs(), " / ", Type.longs(), ")"), - new Operation.Binary(Type.longs(), "(", Type.longs(), " % ", Type.longs(), ")"), - new Operation.Binary(Type.longs(), "(", Type.longs(), " & ", Type.longs(), ")"), - new Operation.Binary(Type.longs(), "(", Type.longs(), " | ", Type.longs(), ")"), - new Operation.Binary(Type.longs(), "(", Type.longs(), " ^ ", Type.longs(), ")"), - new Operation.Binary(Type.longs(), "(", Type.longs(), " << ", Type.longs(), ")"), - new Operation.Binary(Type.longs(), "(", Type.longs(), " >> ", Type.longs(), ")"), - new Operation.Binary(Type.longs(), "(", Type.longs(), " >>> ", Type.longs(), ")"), + new Operation.Binary(Type.longs(), "(", Type.longs(), " + ", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "(", Type.longs(), " - ", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "(", Type.longs(), " * ", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "(", Type.longs(), " / ", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "(", Type.longs(), " % ", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "(", Type.longs(), " & ", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "(", Type.longs(), " | ", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "(", Type.longs(), " ^ ", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "(", Type.longs(), " << ", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "(", Type.longs(), " >> ", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "(", Type.longs(), " >>> ", Type.longs(), ")", null), - new Operation.Unary(Type.longs(), "Byte.toUnsignedLong(", Type.bytes(), ")"), + new Operation.Unary(Type.longs(), "Byte.toUnsignedLong(", Type.bytes(), ")", null), - new Operation.Unary(Type.longs(), "Short.toUnsignedLong(", Type.shorts(), ")"), + new Operation.Unary(Type.longs(), "Short.toUnsignedLong(", Type.shorts(), ")", null), - new Operation.Unary(Type.longs(), "Integer.toUnsignedLong(", Type.ints(), ")"), + new Operation.Unary(Type.longs(), "Integer.toUnsignedLong(", Type.ints(), ")", null), - // new Operation.Binary(Type.longs(), "Long.compress(", Type.longs(), ", ", Type.longs(), ")"), + // new Operation.Binary(Type.longs(), "Long.compress(", Type.longs(), ", ", Type.longs(), ")", null), // TODO: add back after JDK-8350896 - new Operation.Binary(Type.longs(), "Long.divideUnsigned(", Type.longs(), ", ", Type.longs(), ")"), - new Operation.Binary(Type.longs(), "Long.expand(", Type.longs(), ", ", Type.longs(), ")"), - new Operation.Unary(Type.longs(), "Long.highestOneBit(", Type.longs(), ")"), - new Operation.Unary(Type.longs(), "Long.lowestOneBit(", Type.longs(), ")"), - new Operation.Binary(Type.longs(), "Long.min(", Type.longs(), ", ", Type.longs(), ")"), - new Operation.Binary(Type.longs(), "Long.max(", Type.longs(), ", ", Type.longs(), ")"), - new Operation.Binary(Type.longs(), "Long.remainderUnsigned(", Type.longs(), ", ", Type.longs(), ")"), - new Operation.Unary(Type.longs(), "Long.reverse(", Type.longs(), ")"), - new Operation.Unary(Type.longs(), "Long.reverseBytes(", Type.longs(), ")"), - new Operation.Binary(Type.longs(), "Long.rotateLeft(", Type.longs(), ", ", Type.ints(), ")"), - new Operation.Binary(Type.longs(), "Long.rotateRight(", Type.longs(), ", ", Type.ints(), ")"), - new Operation.Binary(Type.longs(), "Long.sum(", Type.longs(), ", ", Type.longs(), ")"), - - new Operation.Unary(Type.longs(), "Double.doubleToLongBits(", Type.doubles(), ")"), + new Operation.Binary(Type.longs(), "Long.divideUnsigned(", Type.longs(), ", ", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "Long.expand(", Type.longs(), ", ", Type.longs(), ")", null), + new Operation.Unary(Type.longs(), "Long.highestOneBit(", Type.longs(), ")", null), + new Operation.Unary(Type.longs(), "Long.lowestOneBit(", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "Long.min(", Type.longs(), ", ", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "Long.max(", Type.longs(), ", ", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "Long.remainderUnsigned(", Type.longs(), ", ", Type.longs(), ")", null), + new Operation.Unary(Type.longs(), "Long.reverse(", Type.longs(), ")", null), + new Operation.Unary(Type.longs(), "Long.reverseBytes(", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "Long.rotateLeft(", Type.longs(), ", ", Type.ints(), ")", null), + new Operation.Binary(Type.longs(), "Long.rotateRight(", Type.longs(), ", ", Type.ints(), ")", null), + new Operation.Binary(Type.longs(), "Long.sum(", Type.longs(), ", ", Type.longs(), ")", null), + + new Operation.Unary(Type.longs(), "Double.doubleToLongBits(", Type.doubles(), ")", null), // Note: Double.doubleToRawLongBits can lead to issues, because the NaN values are not always // represented by the same long bits. - new Operation.Ternary(Type.longs(), "(", Type.booleans(), " ? ", Type.longs(), " : ", Type.longs(), ")") + new Operation.Ternary(Type.longs(), "(", Type.booleans(), " ? ", Type.longs(), " : ", Type.longs(), ")", null) ); private static final List FLOAT_OPERATIONS = List.of( - new Operation.Unary(Type.floats(), "((float)", Type.bytes(), ")"), - new Operation.Unary(Type.floats(), "((float)", Type.chars(), ")"), - new Operation.Unary(Type.floats(), "((float)", Type.shorts(), ")"), - new Operation.Unary(Type.floats(), "((float)", Type.ints(), ")"), - new Operation.Unary(Type.floats(), "((float)", Type.longs(), ")"), - new Operation.Unary(Type.floats(), "((float)", Type.doubles(), ")"), - - new Operation.Unary(Type.floats(), "(-(", Type.floats(), "))"), - - new Operation.Binary(Type.floats(), "(", Type.floats(), " + ", Type.floats(), ")"), - new Operation.Binary(Type.floats(), "(", Type.floats(), " - ", Type.floats(), ")"), - new Operation.Binary(Type.floats(), "(", Type.floats(), " * ", Type.floats(), ")"), - new Operation.Binary(Type.floats(), "(", Type.floats(), " / ", Type.floats(), ")"), - new Operation.Binary(Type.floats(), "(", Type.floats(), " % ", Type.floats(), ")"), - - //new Operation.Unary(Type.floats(), "Float.float16ToFloat(", Type.shorts(), ")"), + new Operation.Unary(Type.floats(), "((float)", Type.bytes(), ")", null), + new Operation.Unary(Type.floats(), "((float)", Type.chars(), ")", null), + new Operation.Unary(Type.floats(), "((float)", Type.shorts(), ")", null), + new Operation.Unary(Type.floats(), "((float)", Type.ints(), ")", null), + new Operation.Unary(Type.floats(), "((float)", Type.longs(), ")", null), + new Operation.Unary(Type.floats(), "((float)", Type.doubles(), ")", null), + + new Operation.Unary(Type.floats(), "(-(", Type.floats(), "))", null), + + new Operation.Binary(Type.floats(), "(", Type.floats(), " + ", Type.floats(), ")", null), + new Operation.Binary(Type.floats(), "(", Type.floats(), " - ", Type.floats(), ")", null), + new Operation.Binary(Type.floats(), "(", Type.floats(), " * ", Type.floats(), ")", null), + new Operation.Binary(Type.floats(), "(", Type.floats(), " / ", Type.floats(), ")", null), + new Operation.Binary(Type.floats(), "(", Type.floats(), " % ", Type.floats(), ")", null), + + //new Operation.Unary(Type.floats(), "Float.float16ToFloat(", Type.shorts(), ")", null), // TODO: add back after JDK-8350835 - new Operation.Unary(Type.floats(), "Float.intBitsToFloat(", Type.ints(), ")"), - new Operation.Binary(Type.floats(), "Float.max(", Type.floats(), ", ", Type.floats(), ")"), - new Operation.Binary(Type.floats(), "Float.min(", Type.floats(), ", ", Type.floats(), ")"), - new Operation.Binary(Type.floats(), "Float.sum(", Type.floats(), ", ", Type.floats(), ")"), + new Operation.Unary(Type.floats(), "Float.intBitsToFloat(", Type.ints(), ")", null), + new Operation.Binary(Type.floats(), "Float.max(", Type.floats(), ", ", Type.floats(), ")", null), + new Operation.Binary(Type.floats(), "Float.min(", Type.floats(), ", ", Type.floats(), ")", null), + new Operation.Binary(Type.floats(), "Float.sum(", Type.floats(), ", ", Type.floats(), ")", null), - new Operation.Ternary(Type.floats(), "(", Type.booleans(), " ? ", Type.floats(), " : ", Type.floats(), ")") + new Operation.Ternary(Type.floats(), "(", Type.booleans(), " ? ", Type.floats(), " : ", Type.floats(), ")", null) ); private static final List DOUBLE_OPERATIONS = List.of( - new Operation.Unary(Type.doubles(), "((double)", Type.bytes(), ")"), - new Operation.Unary(Type.doubles(), "((double)", Type.chars(), ")"), - new Operation.Unary(Type.doubles(), "((double)", Type.shorts(), ")"), - new Operation.Unary(Type.doubles(), "((double)", Type.ints(), ")"), - new Operation.Unary(Type.doubles(), "((double)", Type.longs(), ")"), - new Operation.Unary(Type.doubles(), "((double)", Type.floats(), ")"), + new Operation.Unary(Type.doubles(), "((double)", Type.bytes(), ")", null), + new Operation.Unary(Type.doubles(), "((double)", Type.chars(), ")", null), + new Operation.Unary(Type.doubles(), "((double)", Type.shorts(), ")", null), + new Operation.Unary(Type.doubles(), "((double)", Type.ints(), ")", null), + new Operation.Unary(Type.doubles(), "((double)", Type.longs(), ")", null), + new Operation.Unary(Type.doubles(), "((double)", Type.floats(), ")", null), // Note: There is no cast from boolean - new Operation.Unary(Type.doubles(), "(-(", Type.doubles(), "))"), + new Operation.Unary(Type.doubles(), "(-(", Type.doubles(), "))", null), - new Operation.Binary(Type.doubles(), "(", Type.doubles(), " + ", Type.doubles(), ")"), - new Operation.Binary(Type.doubles(), "(", Type.doubles(), " - ", Type.doubles(), ")"), - new Operation.Binary(Type.doubles(), "(", Type.doubles(), " * ", Type.doubles(), ")"), - new Operation.Binary(Type.doubles(), "(", Type.doubles(), " / ", Type.doubles(), ")"), - new Operation.Binary(Type.doubles(), "(", Type.doubles(), " % ", Type.doubles(), ")"), + new Operation.Binary(Type.doubles(), "(", Type.doubles(), " + ", Type.doubles(), ")", null), + new Operation.Binary(Type.doubles(), "(", Type.doubles(), " - ", Type.doubles(), ")", null), + new Operation.Binary(Type.doubles(), "(", Type.doubles(), " * ", Type.doubles(), ")", null), + new Operation.Binary(Type.doubles(), "(", Type.doubles(), " / ", Type.doubles(), ")", null), + new Operation.Binary(Type.doubles(), "(", Type.doubles(), " % ", Type.doubles(), ")", null), - new Operation.Unary(Type.doubles(), "Double.longBitsToDouble(", Type.ints(), ")"), - new Operation.Binary(Type.doubles(), "Double.max(", Type.doubles(), ", ", Type.doubles(), ")"), - new Operation.Binary(Type.doubles(), "Double.min(", Type.doubles(), ", ", Type.doubles(), ")"), - new Operation.Binary(Type.doubles(), "Double.sum(", Type.doubles(), ", ", Type.doubles(), ")"), + new Operation.Unary(Type.doubles(), "Double.longBitsToDouble(", Type.ints(), ")", null), + new Operation.Binary(Type.doubles(), "Double.max(", Type.doubles(), ", ", Type.doubles(), ")", null), + new Operation.Binary(Type.doubles(), "Double.min(", Type.doubles(), ", ", Type.doubles(), ")", null), + new Operation.Binary(Type.doubles(), "Double.sum(", Type.doubles(), ", ", Type.doubles(), ")", null), - new Operation.Ternary(Type.doubles(), "(", Type.booleans(), " ? ", Type.doubles(), " : ", Type.doubles(), ")") + new Operation.Ternary(Type.doubles(), "(", Type.booleans(), " ? ", Type.doubles(), " : ", Type.doubles(), ")", null) ); private static final List BOOLEAN_OPERATIONS = List.of( // Note: there is no casting / conversion from an to boolean directly. - new Operation.Unary(Type.booleans(), "(!(", Type.booleans(), "))"), + new Operation.Unary(Type.booleans(), "(!(", Type.booleans(), "))", null), - new Operation.Binary(Type.booleans(), "(", Type.booleans(), " || ", Type.booleans(), ")"), - new Operation.Binary(Type.booleans(), "(", Type.booleans(), " && ", Type.booleans(), ")"), - new Operation.Binary(Type.booleans(), "(", Type.booleans(), " ^ ", Type.booleans(), ")"), + new Operation.Binary(Type.booleans(), "(", Type.booleans(), " || ", Type.booleans(), ")", null), + new Operation.Binary(Type.booleans(), "(", Type.booleans(), " && ", Type.booleans(), ")", null), + new Operation.Binary(Type.booleans(), "(", Type.booleans(), " ^ ", Type.booleans(), ")", null), - new Operation.Binary(Type.booleans(), "Boolean.logicalAnd(", Type.booleans(), ", ", Type.booleans(), ")"), - new Operation.Binary(Type.booleans(), "Boolean.logicalOr(", Type.booleans(), ", ", Type.booleans(), ")"), - new Operation.Binary(Type.booleans(), "Boolean.logicalXor(", Type.booleans(), ", ", Type.booleans(), ")"), + new Operation.Binary(Type.booleans(), "Boolean.logicalAnd(", Type.booleans(), ", ", Type.booleans(), ")", null), + new Operation.Binary(Type.booleans(), "Boolean.logicalOr(", Type.booleans(), ", ", Type.booleans(), ")", null), + new Operation.Binary(Type.booleans(), "Boolean.logicalXor(", Type.booleans(), ", ", Type.booleans(), ")", null), // Note: For now, we are omitting all the Character.is<...> methods. We can add them in the future. - new Operation.Unary(Type.booleans(), "Float.isFinite(", Type.floats(), ")"), - new Operation.Unary(Type.booleans(), "Float.isInfinite(", Type.floats(), ")"), - new Operation.Unary(Type.booleans(), "Float.isNaN(", Type.floats(), ")"), + new Operation.Unary(Type.booleans(), "Float.isFinite(", Type.floats(), ")", null), + new Operation.Unary(Type.booleans(), "Float.isInfinite(", Type.floats(), ")", null), + new Operation.Unary(Type.booleans(), "Float.isNaN(", Type.floats(), ")", null), - new Operation.Unary(Type.booleans(), "Double.isFinite(", Type.doubles(), ")"), - new Operation.Unary(Type.booleans(), "Double.isInfinite(", Type.doubles(), ")"), - new Operation.Unary(Type.booleans(), "Double.isNaN(", Type.doubles(), ")"), + new Operation.Unary(Type.booleans(), "Double.isFinite(", Type.doubles(), ")", null), + new Operation.Unary(Type.booleans(), "Double.isInfinite(", Type.doubles(), ")", null), + new Operation.Unary(Type.booleans(), "Double.isNaN(", Type.doubles(), ")", null), - new Operation.Ternary(Type.booleans(), "(", Type.booleans(), " ? ", Type.booleans(), " : ", Type.booleans(), ")") + new Operation.Ternary(Type.booleans(), "(", Type.booleans(), " ? ", Type.booleans(), " : ", Type.booleans(), ")", null) ); public static final List PRIMITIVE_OPERATIONS = Stream.of( @@ -374,32 +374,32 @@ private static final List generateVectorAPIOperations() { List ops = new ArrayList(); for (var type : Type.VECTOR_API_TYPES) { - ops.add(new Operation.Unary(type, "", type, ".abs()")); - ops.add(new Operation.Binary(type, "", type, ".add(", type.elementType, ")")); + ops.add(new Operation.Unary(type, "", type, ".abs()", null)); + ops.add(new Operation.Binary(type, "", type, ".add(", type.elementType, ")", null)); // TODO: add(int e, VectorMask m) - ops.add(new Operation.Binary(type, "", type, ".add(", type, ")")); + ops.add(new Operation.Binary(type, "", type, ".add(", type, ")", null)); // TODO: add(Vector v, VectorMask m) - ops.add(new Operation.Binary(type, "", type, ".addIndex(", Type.ints(), ")")); + ops.add(new Operation.Binary(type, "", type, ".addIndex(", Type.ints(), ")", null)); if (!type.elementType.isFloating()) { - ops.add(new Operation.Binary(type, "", type, ".and(", type.elementType, ")")); - ops.add(new Operation.Binary(type, "", type, ".and(", type, ")")); - ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type.elementType, ", ", type.elementType, ")")); - ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type.elementType, ", ", type, ")")); - ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type, ", ", type.elementType, ")")); - ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type, ", ", type, ")")); + ops.add(new Operation.Binary(type, "", type, ".and(", type.elementType, ")", null)); + ops.add(new Operation.Binary(type, "", type, ".and(", type, ")", null)); + ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type.elementType, ", ", type.elementType, ")", null)); + ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type.elementType, ", ", type, ")", null)); + ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type, ", ", type.elementType, ")", null)); + ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type, ", ", type, ")", null)); } // TODO: blend(int e, VectorMask m) // TODO: blend(long e, VectorMask m) // TODO: blend(Vector v, VectorMask m) - ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", type.elementType, ")")); - ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", Type.longs(), ")")); + ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", type.elementType, ")", null)); + ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", Type.longs(), ")", null)); // TODO: non zero parts for (var type2 : Type.VECTOR_API_TYPES) { - ops.add(new Operation.Unary(type, "((" + type.vectorType + ")", type2 , ".castShape(" + type.species + ", 0))")); + ops.add(new Operation.Unary(type, "((" + type.vectorType + ")", type2 , ".castShape(" + type.species + ", 0))", null)); } // Note: check works on class / species, leaving them out. @@ -414,20 +414,22 @@ private static final List generateVectorAPIOperations() { type2 , ".convert(VectorOperators.Conversion.ofCast(" + type2.elementType.boxedTypeName() + ".class, " - + type.elementType.boxedTypeName() + ".class), 0))")); + + type.elementType.boxedTypeName() + ".class), 0))", + null)); ops.add(new Operation.Unary(type, "((" + type.vectorType + ")", type2 , ".convert(VectorOperators.Conversion.ofReinterpret(" + type2.elementType.boxedTypeName() + ".class, " - + type.elementType.boxedTypeName() + ".class), 0))")); + + type.elementType.boxedTypeName() + ".class), 0))", + null)); // TODO: convertShape } - ops.add(new Operation.Binary(type, "", type, ".div(", type.elementType, ")")); + ops.add(new Operation.Binary(type, "", type, ".div(", type.elementType, ")", null)); // TODO: div(int e, VectorMask m) - ops.add(new Operation.Binary(type, "", type, ".div(", type, ")")); + ops.add(new Operation.Binary(type, "", type, ".div(", type, ")", null)); // TODO: div(Vector v, VectorMask m) // TODO: eq(int e) -> VectorMask @@ -437,11 +439,11 @@ private static final List generateVectorAPIOperations() { // TODO: ensure we use all variants of fromArray and fromMemorySegment, plus intoArray and intoMemorySegment. - ops.add(new Operation.Binary(type.elementType, "", type, ".lane(", Type.ints(), ")")); + ops.add(new Operation.Binary(type.elementType, "", type, ".lane(", Type.ints(), ")", null)); for (VOP vop : VECTOR_API_OPS) { if (vop.args() == 2 && vop.elementTypes().contains(type.elementType)) { - ops.add(new Operation.Binary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ")")); + ops.add(new Operation.Binary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ")", null)); // TODO: lanewise(VectorOperators.Binary op, int e, VectorMask m) } } From e253301add6259905a1852f998f56e4ad17134dd Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 4 Mar 2025 18:59:08 +0100 Subject: [PATCH 144/212] argTypes renaming --- .../lib/template_library/Expression.java | 40 +++++++++---------- .../examples/TestFuzzExpression.java | 2 +- .../examples/TestFuzzVectorAPI.java | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index 5800ba75a0726..19d69dde7a034 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -42,22 +42,22 @@ public final class Expression { private static final Random RANDOM = Utils.getRandomInstance(); private final Template.OneArgs> template; - private final List types; + private final List argTypes; - Expression(final Template.OneArgs> template, final List types) { + Expression(final Template.OneArgs> template, final List argTypes) { this.template = template; - this.types = types; + this.argTypes = argTypes; } - public final List types() { return this.types; } + public final List argTypes() { return this.argTypes; } public final TemplateWithArgs withArgs(List args) { - if (args.size() != types.size()) { throw new RuntimeException("'args' must have the same size as 'types'"); } + if (args.size() != argTypes.size()) { throw new RuntimeException("'args' must have the same size as 'argTypes'"); } return template.withArgs(args); } public final List randomArgValues() { - return types.stream().map(Value::makeRandom).toList(); + return argTypes.stream().map(Value::makeRandom).toList(); } // We would like to use identical args multiple times, but possible field / variable defs have to happen only once. @@ -89,17 +89,17 @@ public static final Expression make(Type resultType, List al HashSet allowedTypesSet = new HashSet(allowedTypes); List ops = Operations.ALL_BUILTIN_OPERATIONS.stream().filter(o -> o.matchesTypes(allowedTypesSet)).toList(); - List types = new ArrayList(); - ExpressionGenerator generator = expressionGenerator(resultType, ops, maxDepth, types); + List argTypes = new ArrayList(); + ExpressionGenerator generator = expressionGenerator(resultType, ops, maxDepth, argTypes); var template = Template.make("args", (List args) -> body( generator.tokens(args) )); - return new Expression(template, types); + return new Expression(template, argTypes); } - private static final ExpressionGenerator expressionGenerator(Type resultType, List ops, int maxDepth, List types) { - ExpressionGeneratorStep step = expressionGeneratorStep(resultType, ops, maxDepth, types); + private static final ExpressionGenerator expressionGenerator(Type resultType, List ops, int maxDepth, List argTypes) { + ExpressionGeneratorStep step = expressionGeneratorStep(resultType, ops, maxDepth, argTypes); return (List args) -> { List tokens = new ArrayList(); step.addTokens(tokens, args); @@ -107,7 +107,7 @@ private static final ExpressionGenerator expressionGenerator(Type resultType, Li }; } - private static final ExpressionGeneratorStep expressionGeneratorStep(Type resultType, List ops, int maxDepth, List types) { + private static final ExpressionGeneratorStep expressionGeneratorStep(Type resultType, List ops, int maxDepth, List argTypes) { List resultTypeOps = ops.stream().filter(o -> o.matchesReturnType(resultType)).toList(); if (maxDepth <= 0 || resultTypeOps.isEmpty() || RANDOM.nextInt(2 * maxDepth) == 0) { if (RANDOM.nextInt(3) == 0) { @@ -118,8 +118,8 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result }; } else { // Remember which type we need to fill the ith argument with. - int i = types.size(); - types.add(resultType); + int i = argTypes.size(); + argTypes.add(resultType); return (List tokens, List args) -> { // Extract the ith argument. tokens.add(args.get(i)); @@ -128,7 +128,7 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result } switch (Library.choice(resultTypeOps)) { case Operation.Unary(Type r, String s0, Type t0, String s1, List> exceptions) -> { - ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, types); + ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, argTypes); return (List tokens, List args) -> { tokens.add(s0); step0.addTokens(tokens, args); @@ -136,8 +136,8 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result }; } case Operation.Binary(Type r, String s0, Type t0, String s1, Type t1, String s2, List> exceptions) -> { - ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, types); - ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, ops, maxDepth-1, types); + ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, argTypes); + ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, ops, maxDepth-1, argTypes); return (List tokens, List args) -> { tokens.add(s0); step0.addTokens(tokens, args); @@ -147,9 +147,9 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result }; } case Operation.Ternary(Type r, String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3, List> exceptions) -> { - ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, types); - ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, ops, maxDepth-1, types); - ExpressionGeneratorStep step2 = expressionGeneratorStep(t1, ops, maxDepth-1, types); + ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, argTypes); + ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, ops, maxDepth-1, argTypes); + ExpressionGeneratorStep step2 = expressionGeneratorStep(t1, ops, maxDepth-1, argTypes); return (List tokens, List args) -> { tokens.add(s0); step0.addTokens(tokens, args); diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index d6b4de7a4d865..de84d045b7182 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -211,7 +211,7 @@ public static String generate(CompileFramework comp) { var template3 = Template.make("type", (Type type)-> { int size = 10_000; // TODO: randomize Expression expression = Expression.make(type, Type.PRIMITIVE_TYPES, 4); - List types = expression.types(); + List types = expression.argTypes(); List arrayDefinitions = new ArrayList<>(); List args = new ArrayList<>(); for (int i = 0; i < types.size(); i++) { diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java index 8703a72e97af6..625936a5071e6 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java @@ -149,7 +149,7 @@ public static String generate(CompileFramework comp) { var template2 = Template.make("type", (VectorAPIType type)-> { int size = 1000; Expression expression = Expression.make(type, Type.ALL_BUILTIN_TYPES, 4); - List types = expression.types(); + List types = expression.argTypes(); List arrayDefinitions = new ArrayList<>(); List args = new ArrayList<>(); for (int i = 0; i < types.size(); i++) { From 77b6230a1cceaead41d039c838bfd8df70007729 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 5 Mar 2025 10:10:21 +0100 Subject: [PATCH 145/212] handle some vector api exceptions --- .../lib/template_library/Expression.java | 37 +++++---- .../lib/template_library/Operation.java | 6 +- .../lib/template_library/Operations.java | 78 ++++++++++--------- .../examples/TestFuzzVectorAPI.java | 9 ++- 4 files changed, 73 insertions(+), 57 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index 19d69dde7a034..1b03f22333282 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -43,13 +43,16 @@ public final class Expression { private final Template.OneArgs> template; private final List argTypes; + private final HashSet exceptions; - Expression(final Template.OneArgs> template, final List argTypes) { + Expression(final Template.OneArgs> template, final List argTypes, HashSet exceptions) { this.template = template; this.argTypes = argTypes; + this.exceptions = exceptions; } public final List argTypes() { return this.argTypes; } + public final HashSet exceptions() { return this.exceptions; } public final TemplateWithArgs withArgs(List args) { if (args.size() != argTypes.size()) { throw new RuntimeException("'args' must have the same size as 'argTypes'"); } @@ -90,16 +93,17 @@ public static final Expression make(Type resultType, List al List ops = Operations.ALL_BUILTIN_OPERATIONS.stream().filter(o -> o.matchesTypes(allowedTypesSet)).toList(); List argTypes = new ArrayList(); - ExpressionGenerator generator = expressionGenerator(resultType, ops, maxDepth, argTypes); + HashSet exceptions = new HashSet(); + ExpressionGenerator generator = expressionGenerator(resultType, ops, maxDepth, argTypes, exceptions); var template = Template.make("args", (List args) -> body( generator.tokens(args) )); - return new Expression(template, argTypes); + return new Expression(template, argTypes, exceptions); } - private static final ExpressionGenerator expressionGenerator(Type resultType, List ops, int maxDepth, List argTypes) { - ExpressionGeneratorStep step = expressionGeneratorStep(resultType, ops, maxDepth, argTypes); + private static final ExpressionGenerator expressionGenerator(Type resultType, List ops, int maxDepth, List argTypes, HashSet exceptions) { + ExpressionGeneratorStep step = expressionGeneratorStep(resultType, ops, maxDepth, argTypes, exceptions); return (List args) -> { List tokens = new ArrayList(); step.addTokens(tokens, args); @@ -107,7 +111,7 @@ private static final ExpressionGenerator expressionGenerator(Type resultType, Li }; } - private static final ExpressionGeneratorStep expressionGeneratorStep(Type resultType, List ops, int maxDepth, List argTypes) { + private static final ExpressionGeneratorStep expressionGeneratorStep(Type resultType, List ops, int maxDepth, List argTypes, HashSet exceptions) { List resultTypeOps = ops.stream().filter(o -> o.matchesReturnType(resultType)).toList(); if (maxDepth <= 0 || resultTypeOps.isEmpty() || RANDOM.nextInt(2 * maxDepth) == 0) { if (RANDOM.nextInt(3) == 0) { @@ -127,17 +131,19 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result } } switch (Library.choice(resultTypeOps)) { - case Operation.Unary(Type r, String s0, Type t0, String s1, List> exceptions) -> { - ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, argTypes); + case Operation.Unary(Type r, String s0, Type t0, String s1, List es) -> { + if (es != null) { exceptions.addAll(es); } + ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, argTypes, exceptions); return (List tokens, List args) -> { tokens.add(s0); step0.addTokens(tokens, args); tokens.add(s1); }; } - case Operation.Binary(Type r, String s0, Type t0, String s1, Type t1, String s2, List> exceptions) -> { - ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, argTypes); - ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, ops, maxDepth-1, argTypes); + case Operation.Binary(Type r, String s0, Type t0, String s1, Type t1, String s2, List es) -> { + if (es != null) { exceptions.addAll(es); } + ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, argTypes, exceptions); + ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, ops, maxDepth-1, argTypes, exceptions); return (List tokens, List args) -> { tokens.add(s0); step0.addTokens(tokens, args); @@ -146,10 +152,11 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result tokens.add(s2); }; } - case Operation.Ternary(Type r, String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3, List> exceptions) -> { - ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, argTypes); - ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, ops, maxDepth-1, argTypes); - ExpressionGeneratorStep step2 = expressionGeneratorStep(t1, ops, maxDepth-1, argTypes); + case Operation.Ternary(Type r, String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3, List es) -> { + if (es != null) { exceptions.addAll(es); } + ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, argTypes, exceptions); + ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, ops, maxDepth-1, argTypes, exceptions); + ExpressionGeneratorStep step2 = expressionGeneratorStep(t1, ops, maxDepth-1, argTypes, exceptions); return (List tokens, List args) -> { tokens.add(s0); step0.addTokens(tokens, args); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operation.java b/test/hotspot/jtreg/compiler/lib/template_library/Operation.java index eea5b3f00b98b..c5c86d92fc21f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operation.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operation.java @@ -38,7 +38,7 @@ public sealed interface Operation permits Operation.Unary, public abstract boolean matchesTypes(HashSet types); - public static record Unary(Type r, String s0, Type t0, String s1, List> exceptions) implements Operation { + public static record Unary(Type r, String s0, Type t0, String s1, List exceptions) implements Operation { @Override public boolean matchesReturnType(Type returnType) { return r.isSubtypeOf(returnType); @@ -51,7 +51,7 @@ public boolean matchesTypes(HashSet types) { } } - public static record Binary(Type r, String s0, Type t0, String s1, Type t1, String s2, List> exceptions) implements Operation { + public static record Binary(Type r, String s0, Type t0, String s1, Type t1, String s2, List exceptions) implements Operation { @Override public boolean matchesReturnType(Type returnType) { return r.isSubtypeOf(returnType); @@ -65,7 +65,7 @@ public boolean matchesTypes(HashSet types) { } } - public static record Ternary(Type r, String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3, List> exceptions) implements Operation { + public static record Ternary(Type r, String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3, List exceptions) implements Operation { @Override public boolean matchesReturnType(Type returnType) { return r.isSubtypeOf(returnType); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 08a80446fe2ce..d435f0af57ad3 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -313,33 +313,34 @@ final class Operations { private record VOP(String name, int args, List elementTypes) {} + // TODO: consider some floating results as inexact, and handle it accordingly? private static final List VECTOR_API_OPS = List.of( new VOP("ABS", 1, Type.PRIMITIVE_TYPES), - new VOP("ACOS", 1, Type.FLOATING_TYPES), + //new VOP("ACOS", 1, Type.FLOATING_TYPES), new VOP("ADD", 2, Type.PRIMITIVE_TYPES), new VOP("AND", 2, Type.INTEGRAL_TYPES), new VOP("AND_NOT", 2, Type.INTEGRAL_TYPES), new VOP("ASHR", 2, Type.INTEGRAL_TYPES), - new VOP("ASIN", 1, Type.FLOATING_TYPES), - new VOP("ATAN", 1, Type.FLOATING_TYPES), - new VOP("ATAN2", 2, Type.FLOATING_TYPES), + //new VOP("ASIN", 1, Type.FLOATING_TYPES), + //new VOP("ATAN", 1, Type.FLOATING_TYPES), + //new VOP("ATAN2", 2, Type.FLOATING_TYPES), new VOP("BIT_COUNT", 1, Type.INTEGRAL_TYPES), new VOP("BITWISE_BLEND", 3, Type.INTEGRAL_TYPES), - new VOP("CBRT", 1, Type.FLOATING_TYPES), + //new VOP("CBRT", 1, Type.FLOATING_TYPES), new VOP("COMPRESS_BITS", 2, Type.INT_LONG_TYPES), - new VOP("COS", 1, Type.FLOATING_TYPES), - new VOP("COSH", 1, Type.FLOATING_TYPES), + //new VOP("COS", 1, Type.FLOATING_TYPES), + //new VOP("COSH", 1, Type.FLOATING_TYPES), new VOP("DIV", 2, Type.FLOATING_TYPES), - new VOP("EXP", 1, Type.FLOATING_TYPES), + //new VOP("EXP", 1, Type.FLOATING_TYPES), new VOP("EXPAND_BITS", 2, Type.INT_LONG_TYPES), - new VOP("EXPM1", 1, Type.FLOATING_TYPES), + //new VOP("EXPM1", 1, Type.FLOATING_TYPES), new VOP("FIRST_NONZERO", 1, Type.PRIMITIVE_TYPES), new VOP("FMA", 3, Type.FLOATING_TYPES), - new VOP("HYPOT", 2, Type.FLOATING_TYPES), + //new VOP("HYPOT", 2, Type.FLOATING_TYPES), new VOP("LEADING_ZEROS_COUNT", 1, Type.PRIMITIVE_TYPES), - new VOP("LOG", 1, Type.FLOATING_TYPES), - new VOP("LOG10", 1, Type.FLOATING_TYPES), - new VOP("LOG1P", 1, Type.FLOATING_TYPES), + //new VOP("LOG", 1, Type.FLOATING_TYPES), + //new VOP("LOG10", 1, Type.FLOATING_TYPES), + //new VOP("LOG1P", 1, Type.FLOATING_TYPES), new VOP("LSHL", 2, Type.INTEGRAL_TYPES), new VOP("LSHR", 2, Type.INTEGRAL_TYPES), new VOP("MIN", 2, Type.PRIMITIVE_TYPES), @@ -348,21 +349,21 @@ private record VOP(String name, int args, List elementTypes) {} new VOP("NEG", 1, Type.PRIMITIVE_TYPES), new VOP("NOT", 1, Type.INTEGRAL_TYPES), new VOP("OR", 2, Type.INTEGRAL_TYPES), - new VOP("POW", 2, Type.FLOATING_TYPES), + //new VOP("POW", 2, Type.FLOATING_TYPES), new VOP("REVERSE", 1, Type.PRIMITIVE_TYPES), new VOP("REVERSE_BYTES", 1, Type.PRIMITIVE_TYPES), new VOP("ROL", 2, Type.INTEGRAL_TYPES), new VOP("ROR", 2, Type.INTEGRAL_TYPES), new VOP("SADD", 2, Type.INTEGRAL_TYPES), - new VOP("SIN", 1, Type.FLOATING_TYPES), - new VOP("SINH", 1, Type.FLOATING_TYPES), - new VOP("SQRT", 1, Type.FLOATING_TYPES), + //new VOP("SIN", 1, Type.FLOATING_TYPES), + //new VOP("SINH", 1, Type.FLOATING_TYPES), + //new VOP("SQRT", 1, Type.FLOATING_TYPES), new VOP("SSUB", 2, Type.INTEGRAL_TYPES), new VOP("SUADD", 2, Type.INTEGRAL_TYPES), new VOP("SUB", 2, Type.PRIMITIVE_TYPES), new VOP("SUSUB", 2, Type.INTEGRAL_TYPES), - new VOP("TAN", 1, Type.FLOATING_TYPES), - new VOP("TANH", 1, Type.FLOATING_TYPES), + //new VOP("TAN", 1, Type.FLOATING_TYPES), + //new VOP("TANH", 1, Type.FLOATING_TYPES), new VOP("TRAILING_ZEROS_COUNT", 1, Type.PRIMITIVE_TYPES), new VOP("UMAX", 2, Type.INTEGRAL_TYPES), new VOP("UMIN", 2, Type.INTEGRAL_TYPES), @@ -379,7 +380,8 @@ private static final List generateVectorAPIOperations() { // TODO: add(int e, VectorMask m) ops.add(new Operation.Binary(type, "", type, ".add(", type, ")", null)); // TODO: add(Vector v, VectorMask m) - ops.add(new Operation.Binary(type, "", type, ".addIndex(", Type.ints(), ")", null)); + // FIXME: addIndex bounds + //ops.add(new Operation.Binary(type, "", type, ".addIndex(", Type.ints(), ")", null)); if (!type.elementType.isFloating()) { ops.add(new Operation.Binary(type, "", type, ".and(", type.elementType, ")", null)); @@ -395,7 +397,7 @@ private static final List generateVectorAPIOperations() { // TODO: blend(Vector v, VectorMask m) ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", type.elementType, ")", null)); - ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", Type.longs(), ")", null)); + ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", Type.longs(), ")", List.of("IllegalArgumentException"))); // TODO: non zero parts for (var type2 : Type.VECTOR_API_TYPES) { @@ -409,27 +411,28 @@ private static final List generateVectorAPIOperations() { // TODO: non zero parts for (var type2 : Type.VECTOR_API_TYPES) { - ops.add(new Operation.Unary(type, - "((" + type.vectorType + ")", - type2 , - ".convert(VectorOperators.Conversion.ofCast(" - + type2.elementType.boxedTypeName() + ".class, " - + type.elementType.boxedTypeName() + ".class), 0))", - null)); - ops.add(new Operation.Unary(type, - "((" + type.vectorType + ")", - type2 , - ".convert(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.boxedTypeName() + ".class, " - + type.elementType.boxedTypeName() + ".class), 0))", - null)); + // FIXME: fix shape compatibility + // ops.add(new Operation.Unary(type, + // "((" + type.vectorType + ")", + // type2 , + // ".convert(VectorOperators.Conversion.ofCast(" + // + type2.elementType.name() + ".class, " + // + type.elementType.name() + ".class), 0))", + // null)); + // ops.add(new Operation.Unary(type, + // "((" + type.vectorType + ")", + // type2 , + // ".convert(VectorOperators.Conversion.ofReinterpret(" + // + type2.elementType.name() + ".class, " + // + type.elementType.name() + ".class), 0))", + // null)); // TODO: convertShape } ops.add(new Operation.Binary(type, "", type, ".div(", type.elementType, ")", null)); // TODO: div(int e, VectorMask m) - ops.add(new Operation.Binary(type, "", type, ".div(", type, ")", null)); + ops.add(new Operation.Binary(type, "", type, ".div(", type, ")", List.of("ArithmeticException"))); // TODO: div(Vector v, VectorMask m) // TODO: eq(int e) -> VectorMask @@ -439,7 +442,8 @@ private static final List generateVectorAPIOperations() { // TODO: ensure we use all variants of fromArray and fromMemorySegment, plus intoArray and intoMemorySegment. - ops.add(new Operation.Binary(type.elementType, "", type, ".lane(", Type.ints(), ")", null)); + // TODO: lane case that is allowed to throw java.lang.IllegalArgumentException for out of bonds. + ops.add(new Operation.Binary(type.elementType, "", type, ".lane(", Type.ints(), " & " + (type.length-1) + ")", null)); for (VOP vop : VECTOR_API_OPS) { if (vop.args() == 2 && vop.elementTypes().contains(type.elementType)) { diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java index 625936a5071e6..c9695649fe0b4 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java @@ -191,8 +191,13 @@ public static String generate(CompileFramework comp) { " ", expression.withArgs(args), ".intoArray(out, 0);\n", """ return out; - } catch (Exception e) { - return e; + """, + expression.exceptions().stream().map(exception -> + "} catch (" + exception + " e) { return e;\n" + ).toList(), + """ + } finally { + // Just javac is happy if there are no exceptions to catch. } } From a4b28ef2a137323de918ad211d3f5bf79e1f5cbb Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 5 Mar 2025 10:21:27 +0100 Subject: [PATCH 146/212] TestFuzzExpression.java restrict the exceptions we catch --- .../lib/template_library/Operations.java | 17 +++++++------- .../examples/TestFuzzExpression.java | 23 ++++++++++++++----- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index d435f0af57ad3..7d9235ddf5691 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.stream.Stream; +// TODO: find more operations, like in Math.java or ExactMath.java final class Operations { private static final List BYTE_OPERATIONS = List.of( @@ -111,8 +112,8 @@ final class Operations { new Operation.Binary(Type.ints(), "(", Type.ints(), " + ", Type.ints(), ")", null), new Operation.Binary(Type.ints(), "(", Type.ints(), " - ", Type.ints(), ")", null), new Operation.Binary(Type.ints(), "(", Type.ints(), " * ", Type.ints(), ")", null), - new Operation.Binary(Type.ints(), "(", Type.ints(), " / ", Type.ints(), ")", null), - new Operation.Binary(Type.ints(), "(", Type.ints(), " % ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "(", Type.ints(), " / ", Type.ints(), ")", List.of("ArithmeticException")), + new Operation.Binary(Type.ints(), "(", Type.ints(), " % ", Type.ints(), ")", List.of("ArithmeticException")), new Operation.Binary(Type.ints(), "(", Type.ints(), " & ", Type.ints(), ")", null), new Operation.Binary(Type.ints(), "(", Type.ints(), " | ", Type.ints(), ")", null), new Operation.Binary(Type.ints(), "(", Type.ints(), " ^ ", Type.ints(), ")", null), @@ -135,7 +136,7 @@ final class Operations { new Operation.Binary(Type.ints(), "Integer.compareUnsigned(", Type.ints(), ", ", Type.ints(), ")", null), //new Operation.Binary(Type.ints(), "Integer.compress(", Type.ints(), ", ", Type.ints(), ")", null), // TODO: add back after JDK-8350896 - new Operation.Binary(Type.ints(), "Integer.divideUnsigned(", Type.ints(), ", ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "Integer.divideUnsigned(", Type.ints(), ", ", Type.ints(), ")", List.of("ArithmeticException")), new Operation.Binary(Type.ints(), "Integer.expand(", Type.ints(), ", ", Type.ints(), ")", null), new Operation.Unary(Type.ints(), "Integer.highestOneBit(", Type.ints(), ")", null), new Operation.Unary(Type.ints(), "Integer.lowestOneBit(", Type.ints(), ")", null), @@ -143,7 +144,7 @@ final class Operations { new Operation.Binary(Type.ints(), "Integer.max(", Type.ints(), ", ", Type.ints(), ")", null), new Operation.Unary(Type.ints(), "Integer.numberOfLeadingZeros(", Type.ints(), ")", null), new Operation.Unary(Type.ints(), "Integer.numberOfTrailingZeros(", Type.ints(), ")", null), - new Operation.Binary(Type.ints(), "Integer.remainderUnsigned(", Type.ints(), ", ", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "Integer.remainderUnsigned(", Type.ints(), ", ", Type.ints(), ")", List.of("ArithmeticException")), new Operation.Unary(Type.ints(), "Integer.reverse(", Type.ints(), ")", null), new Operation.Unary(Type.ints(), "Integer.reverseBytes(", Type.ints(), ")", null), new Operation.Binary(Type.ints(), "Integer.rotateLeft(", Type.ints(), ", ", Type.ints(), ")", null), @@ -185,8 +186,8 @@ final class Operations { new Operation.Binary(Type.longs(), "(", Type.longs(), " + ", Type.longs(), ")", null), new Operation.Binary(Type.longs(), "(", Type.longs(), " - ", Type.longs(), ")", null), new Operation.Binary(Type.longs(), "(", Type.longs(), " * ", Type.longs(), ")", null), - new Operation.Binary(Type.longs(), "(", Type.longs(), " / ", Type.longs(), ")", null), - new Operation.Binary(Type.longs(), "(", Type.longs(), " % ", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "(", Type.longs(), " / ", Type.longs(), ")", List.of("ArithmeticException")), + new Operation.Binary(Type.longs(), "(", Type.longs(), " % ", Type.longs(), ")", List.of("ArithmeticException")), new Operation.Binary(Type.longs(), "(", Type.longs(), " & ", Type.longs(), ")", null), new Operation.Binary(Type.longs(), "(", Type.longs(), " | ", Type.longs(), ")", null), new Operation.Binary(Type.longs(), "(", Type.longs(), " ^ ", Type.longs(), ")", null), @@ -202,13 +203,13 @@ final class Operations { // new Operation.Binary(Type.longs(), "Long.compress(", Type.longs(), ", ", Type.longs(), ")", null), // TODO: add back after JDK-8350896 - new Operation.Binary(Type.longs(), "Long.divideUnsigned(", Type.longs(), ", ", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "Long.divideUnsigned(", Type.longs(), ", ", Type.longs(), ")", List.of("ArithmeticException")), new Operation.Binary(Type.longs(), "Long.expand(", Type.longs(), ", ", Type.longs(), ")", null), new Operation.Unary(Type.longs(), "Long.highestOneBit(", Type.longs(), ")", null), new Operation.Unary(Type.longs(), "Long.lowestOneBit(", Type.longs(), ")", null), new Operation.Binary(Type.longs(), "Long.min(", Type.longs(), ", ", Type.longs(), ")", null), new Operation.Binary(Type.longs(), "Long.max(", Type.longs(), ", ", Type.longs(), ")", null), - new Operation.Binary(Type.longs(), "Long.remainderUnsigned(", Type.longs(), ", ", Type.longs(), ")", null), + new Operation.Binary(Type.longs(), "Long.remainderUnsigned(", Type.longs(), ", ", Type.longs(), ")", List.of("ArithmeticException")), new Operation.Unary(Type.longs(), "Long.reverse(", Type.longs(), ")", null), new Operation.Unary(Type.longs(), "Long.reverseBytes(", Type.longs(), ")", null), new Operation.Binary(Type.longs(), "Long.rotateLeft(", Type.longs(), ", ", Type.ints(), ")", null), diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index de84d045b7182..0971c1702addc 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -117,9 +117,12 @@ public static String generate(CompileFramework comp) { try { """, " return ", expression.withArgs(use), ";\n", + expression.exceptions().stream().map(exception -> + "} catch (" + exception + " e) { return e;\n" + ).toList(), """ - } catch (Exception e) { - return e; + } finally { + // Just javac is happy if there are no exceptions to catch. } } @@ -160,9 +163,12 @@ public static String generate(CompileFramework comp) { try { """, " return ", expression.withRandomArgs(), ";\n", + expression.exceptions().stream().map(exception -> + "} catch (" + exception + " e) { return e;\n" + ).toList(), """ - } catch (Exception e) { - return e; + } finally { + // Just javac is happy if there are no exceptions to catch. } """ ); @@ -245,8 +251,13 @@ public static String generate(CompileFramework comp) { """ } return out; - } catch (Exception e) { - return e; + """, + expression.exceptions().stream().map(exception -> + "} catch (" + exception + " e) { return e;\n" + ).toList(), + """ + } finally { + // Just javac is happy if there are no exceptions to catch. } } From 4589b93cb354d5065c4f70ef1de96a3cce795637 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 5 Mar 2025 10:31:44 +0100 Subject: [PATCH 147/212] tweak --- .../hotspot/jtreg/compiler/lib/template_library/Operations.java | 2 +- .../template_library/examples/TestFuzzVectorAPI.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 7d9235ddf5691..070a164070f1b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -431,7 +431,7 @@ private static final List generateVectorAPIOperations() { // TODO: convertShape } - ops.add(new Operation.Binary(type, "", type, ".div(", type.elementType, ")", null)); + ops.add(new Operation.Binary(type, "", type, ".div(", type.elementType, ")", List.of("ArithmeticException"))); // TODO: div(int e, VectorMask m) ops.add(new Operation.Binary(type, "", type, ".div(", type, ")", List.of("ArithmeticException"))); // TODO: div(Vector v, VectorMask m) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java index c9695649fe0b4..71be117c16e1e 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java @@ -217,7 +217,7 @@ public static String generate(CompileFramework comp) { templates.add(Library.arrayFillMethods()); for (VectorAPIType type : Type.VECTOR_API_TYPES) { for (int i = 0; i < 10; i++) { templates.add(template1.withArgs(type)); } - for (int i = 0; i < 5; i++) { templates.add(template2.withArgs(type)); } + for (int i = 0; i < 10; i++) { templates.add(template2.withArgs(type)); } } return IRTestClass.TEMPLATE.withArgs(info, templates).render(); } From d199aaaa1791b342e8b5a1e8a4b598328853a3aa Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 5 Mar 2025 10:47:37 +0100 Subject: [PATCH 148/212] fix convert --- .../lib/template_library/Operations.java | 32 ++++++++++--------- .../lib/template_library/VectorAPIType.java | 2 ++ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 070a164070f1b..48ea06cccbca2 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -412,21 +412,23 @@ private static final List generateVectorAPIOperations() { // TODO: non zero parts for (var type2 : Type.VECTOR_API_TYPES) { - // FIXME: fix shape compatibility - // ops.add(new Operation.Unary(type, - // "((" + type.vectorType + ")", - // type2 , - // ".convert(VectorOperators.Conversion.ofCast(" - // + type2.elementType.name() + ".class, " - // + type.elementType.name() + ".class), 0))", - // null)); - // ops.add(new Operation.Unary(type, - // "((" + type.vectorType + ")", - // type2 , - // ".convert(VectorOperators.Conversion.ofReinterpret(" - // + type2.elementType.name() + ".class, " - // + type.elementType.name() + ".class), 0))", - // null)); + // "convert" keeps the same shape, i.e. length of the vector in bits. + if (type.sizeInBits() == type2.sizeInBits()) { + ops.add(new Operation.Unary(type, + "((" + type.vectorType + ")", + type2 , + ".convert(VectorOperators.Conversion.ofCast(" + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), 0))", + null)); + ops.add(new Operation.Unary(type, + "((" + type.vectorType + ")", + type2 , + ".convert(VectorOperators.Conversion.ofReinterpret(" + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), 0))", + null)); + } // TODO: convertShape } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java index 967066b13f35f..37ec508ba3256 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java @@ -89,4 +89,6 @@ public final Object con() { if (r == 0) { return vectorType + ".zero(" + species + ")"; } return List.of(vectorType, ".broadcast(", species, ", ", elementType.con(), ")"); } + + public final int sizeInBits() { return length * elementType.sizeInBits(); } } From 158c82495f2903ead348ee640edf54138425583f Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 5 Mar 2025 10:56:16 +0100 Subject: [PATCH 149/212] handle addIndex --- .../jtreg/compiler/lib/template_library/Operations.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 48ea06cccbca2..d68e151d3ed52 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -381,8 +381,10 @@ private static final List generateVectorAPIOperations() { // TODO: add(int e, VectorMask m) ops.add(new Operation.Binary(type, "", type, ".add(", type, ")", null)); // TODO: add(Vector v, VectorMask m) - // FIXME: addIndex bounds - //ops.add(new Operation.Binary(type, "", type, ".addIndex(", Type.ints(), ")", null)); + + // If VLENGTH*scale overflows, then a IllegalArgumentException is thrown. + ops.add(new Operation.Binary(type, "", type, ".addIndex(", Type.ints(), " & 0xFFFF)", null)); + ops.add(new Operation.Binary(type, "", type, ".addIndex(", Type.ints(), ")", List.of("IllegalArgumentException"))); if (!type.elementType.isFloating()) { ops.add(new Operation.Binary(type, "", type, ".and(", type.elementType, ")", null)); From d9958c4a2006861e94e12a1d9a0e0e4070c3ada1 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 5 Mar 2025 11:29:07 +0100 Subject: [PATCH 150/212] vector api unary and ternary ops --- .../lib/template_library/Operations.java | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index d68e151d3ed52..d2e494b8dbc60 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -335,10 +335,10 @@ private record VOP(String name, int args, List elementTypes) {} //new VOP("EXP", 1, Type.FLOATING_TYPES), new VOP("EXPAND_BITS", 2, Type.INT_LONG_TYPES), //new VOP("EXPM1", 1, Type.FLOATING_TYPES), - new VOP("FIRST_NONZERO", 1, Type.PRIMITIVE_TYPES), + new VOP("FIRST_NONZERO", 2, Type.PRIMITIVE_TYPES), new VOP("FMA", 3, Type.FLOATING_TYPES), //new VOP("HYPOT", 2, Type.FLOATING_TYPES), - new VOP("LEADING_ZEROS_COUNT", 1, Type.PRIMITIVE_TYPES), + new VOP("LEADING_ZEROS_COUNT", 1, Type.INTEGRAL_TYPES), //new VOP("LOG", 1, Type.FLOATING_TYPES), //new VOP("LOG10", 1, Type.FLOATING_TYPES), //new VOP("LOG1P", 1, Type.FLOATING_TYPES), @@ -351,8 +351,8 @@ private record VOP(String name, int args, List elementTypes) {} new VOP("NOT", 1, Type.INTEGRAL_TYPES), new VOP("OR", 2, Type.INTEGRAL_TYPES), //new VOP("POW", 2, Type.FLOATING_TYPES), - new VOP("REVERSE", 1, Type.PRIMITIVE_TYPES), - new VOP("REVERSE_BYTES", 1, Type.PRIMITIVE_TYPES), + new VOP("REVERSE", 1, Type.INTEGRAL_TYPES), + new VOP("REVERSE_BYTES", 1, Type.INTEGRAL_TYPES), new VOP("ROL", 2, Type.INTEGRAL_TYPES), new VOP("ROR", 2, Type.INTEGRAL_TYPES), new VOP("SADD", 2, Type.INTEGRAL_TYPES), @@ -365,7 +365,7 @@ private record VOP(String name, int args, List elementTypes) {} new VOP("SUSUB", 2, Type.INTEGRAL_TYPES), //new VOP("TAN", 1, Type.FLOATING_TYPES), //new VOP("TANH", 1, Type.FLOATING_TYPES), - new VOP("TRAILING_ZEROS_COUNT", 1, Type.PRIMITIVE_TYPES), + new VOP("TRAILING_ZEROS_COUNT", 1, Type.INTEGRAL_TYPES), new VOP("UMAX", 2, Type.INTEGRAL_TYPES), new VOP("UMIN", 2, Type.INTEGRAL_TYPES), new VOP("XOR", 2, Type.INTEGRAL_TYPES), @@ -383,7 +383,8 @@ private static final List generateVectorAPIOperations() { // TODO: add(Vector v, VectorMask m) // If VLENGTH*scale overflows, then a IllegalArgumentException is thrown. - ops.add(new Operation.Binary(type, "", type, ".addIndex(", Type.ints(), " & 0xFFFF)", null)); + int addIndexBits = type.elementType.sizeInBits() / type.length; + ops.add(new Operation.Binary(type, "", type, ".addIndex(", Type.ints(), " & " + (addIndexBits-1) + ")", null)); ops.add(new Operation.Binary(type, "", type, ".addIndex(", Type.ints(), ")", List.of("IllegalArgumentException"))); if (!type.elementType.isFloating()) { @@ -451,9 +452,27 @@ private static final List generateVectorAPIOperations() { ops.add(new Operation.Binary(type.elementType, "", type, ".lane(", Type.ints(), " & " + (type.length-1) + ")", null)); for (VOP vop : VECTOR_API_OPS) { + if (vop.args() == 1 && vop.elementTypes().contains(type.elementType)) { + ops.add(new Operation.Unary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ")", null)); + // TODO: lanewise(VectorOperators.Unary op, VectorMask m) + } if (vop.args() == 2 && vop.elementTypes().contains(type.elementType)) { ops.add(new Operation.Binary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ")", null)); // TODO: lanewise(VectorOperators.Binary op, int e, VectorMask m) + // TODO: lanewise(VectorOperators.Binary op, long e) + // TODO: lanewise(VectorOperators.Binary op, long e, VectorMask m) + ops.add(new Operation.Binary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ")", null)); + // TODO: lanewise(VectorOperators.Binary op, Vector v, VectorMask m) + } + if (vop.args() == 3 && vop.elementTypes().contains(type.elementType)) { + ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type.elementType, ")", null)); + // TODO: lanewise(VectorOperators.Ternary op, int e1, int e2, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type, ")", null)); + // TODO: lanewise(VectorOperators.Ternary op, int e1, Vector v2, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ", ", type.elementType, ")", null)); + // TODO: lanewise(VectorOperators.Ternary op, Vector v1, int e2, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ", ", type, ")", null)); + // TODO: lanewise(VectorOperators.Ternary op, Vector v1, Vector v2, VectorMask m) } } } From f187489b866693f0c4c5aa4e107a9c65b873f9b4 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 5 Mar 2025 12:01:09 +0100 Subject: [PATCH 151/212] more ops --- .../lib/template_library/Operations.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index d2e494b8dbc60..ba508f1132b02 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -383,8 +383,7 @@ private static final List generateVectorAPIOperations() { // TODO: add(Vector v, VectorMask m) // If VLENGTH*scale overflows, then a IllegalArgumentException is thrown. - int addIndexBits = type.elementType.sizeInBits() / type.length; - ops.add(new Operation.Binary(type, "", type, ".addIndex(", Type.ints(), " & " + (addIndexBits-1) + ")", null)); + ops.add(new Operation.Unary(type, "", type, ".addIndex(1)", null)); ops.add(new Operation.Binary(type, "", type, ".addIndex(", Type.ints(), ")", List.of("IllegalArgumentException"))); if (!type.elementType.isFloating()) { @@ -394,6 +393,9 @@ private static final List generateVectorAPIOperations() { ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type.elementType, ", ", type, ")", null)); ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type, ", ", type.elementType, ")", null)); ops.add(new Operation.Ternary(type, "", type, ".bitwiseBlend(", type, ", ", type, ")", null)); + ops.add(new Operation.Unary(type, "", type, ".not()", null)); + ops.add(new Operation.Binary(type, "", type, ".or(", type.elementType, ")", null)); + ops.add(new Operation.Binary(type, "", type, ".or(", type, ")", null)); } // TODO: blend(int e, VectorMask m) @@ -475,6 +477,24 @@ private static final List generateVectorAPIOperations() { // TODO: lanewise(VectorOperators.Ternary op, Vector v1, Vector v2, VectorMask m) } } + + // TODO: lt, maskAll -> VectorMask + + ops.add(new Operation.Binary(type, "", type, ".max(", type.elementType, ")", null)); + ops.add(new Operation.Binary(type, "", type, ".max(", type, ")", null)); + ops.add(new Operation.Binary(type, "", type, ".min(", type.elementType, ")", null)); + ops.add(new Operation.Binary(type, "", type, ".min(", type, ")", null)); + + ops.add(new Operation.Binary(type, "", type, ".mul(", type.elementType, ")", null)); + // TODO: mul(int e, VectorMask m) + ops.add(new Operation.Binary(type, "", type, ".mul(", type, ")", null)); + // TODO: mul(Vector v, VectorMask m) + + ops.add(new Operation.Unary(type, "", type, ".neg()", null)); + + // TODO: rearrange(VectorShuffle shuffle) + // TODO: rearrange(VectorShuffle s, Vector v) + // TODO: rearrange(VectorShuffle s, VectorMask m) } // Ensure the list is immutable. From 884b932c14c58c63bf3c3d5df63b1ab82ef97bbe Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 6 Mar 2025 08:57:16 +0100 Subject: [PATCH 152/212] VOPType refactor --- .../lib/template_library/Operations.java | 160 +++++++++--------- .../examples/TestFuzzVectorAPI.java | 3 + 2 files changed, 86 insertions(+), 77 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index ba508f1132b02..3b99c1a42fc8e 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -311,65 +311,65 @@ final class Operations { BOOLEAN_OPERATIONS ).flatMap((List l) -> l.stream()).toList(); - - private record VOP(String name, int args, List elementTypes) {} + private enum VOPType { UNARY, BINARY, ASSOCIATIVE, TERNARY } + private record VOP(String name, VOPType type, List elementTypes) {} // TODO: consider some floating results as inexact, and handle it accordingly? private static final List VECTOR_API_OPS = List.of( - new VOP("ABS", 1, Type.PRIMITIVE_TYPES), - //new VOP("ACOS", 1, Type.FLOATING_TYPES), - new VOP("ADD", 2, Type.PRIMITIVE_TYPES), - new VOP("AND", 2, Type.INTEGRAL_TYPES), - new VOP("AND_NOT", 2, Type.INTEGRAL_TYPES), - new VOP("ASHR", 2, Type.INTEGRAL_TYPES), - //new VOP("ASIN", 1, Type.FLOATING_TYPES), - //new VOP("ATAN", 1, Type.FLOATING_TYPES), - //new VOP("ATAN2", 2, Type.FLOATING_TYPES), - new VOP("BIT_COUNT", 1, Type.INTEGRAL_TYPES), - new VOP("BITWISE_BLEND", 3, Type.INTEGRAL_TYPES), - //new VOP("CBRT", 1, Type.FLOATING_TYPES), - new VOP("COMPRESS_BITS", 2, Type.INT_LONG_TYPES), - //new VOP("COS", 1, Type.FLOATING_TYPES), - //new VOP("COSH", 1, Type.FLOATING_TYPES), - new VOP("DIV", 2, Type.FLOATING_TYPES), - //new VOP("EXP", 1, Type.FLOATING_TYPES), - new VOP("EXPAND_BITS", 2, Type.INT_LONG_TYPES), - //new VOP("EXPM1", 1, Type.FLOATING_TYPES), - new VOP("FIRST_NONZERO", 2, Type.PRIMITIVE_TYPES), - new VOP("FMA", 3, Type.FLOATING_TYPES), - //new VOP("HYPOT", 2, Type.FLOATING_TYPES), - new VOP("LEADING_ZEROS_COUNT", 1, Type.INTEGRAL_TYPES), - //new VOP("LOG", 1, Type.FLOATING_TYPES), - //new VOP("LOG10", 1, Type.FLOATING_TYPES), - //new VOP("LOG1P", 1, Type.FLOATING_TYPES), - new VOP("LSHL", 2, Type.INTEGRAL_TYPES), - new VOP("LSHR", 2, Type.INTEGRAL_TYPES), - new VOP("MIN", 2, Type.PRIMITIVE_TYPES), - new VOP("MAX", 2, Type.PRIMITIVE_TYPES), - new VOP("MUL", 2, Type.PRIMITIVE_TYPES), - new VOP("NEG", 1, Type.PRIMITIVE_TYPES), - new VOP("NOT", 1, Type.INTEGRAL_TYPES), - new VOP("OR", 2, Type.INTEGRAL_TYPES), - //new VOP("POW", 2, Type.FLOATING_TYPES), - new VOP("REVERSE", 1, Type.INTEGRAL_TYPES), - new VOP("REVERSE_BYTES", 1, Type.INTEGRAL_TYPES), - new VOP("ROL", 2, Type.INTEGRAL_TYPES), - new VOP("ROR", 2, Type.INTEGRAL_TYPES), - new VOP("SADD", 2, Type.INTEGRAL_TYPES), - //new VOP("SIN", 1, Type.FLOATING_TYPES), - //new VOP("SINH", 1, Type.FLOATING_TYPES), - //new VOP("SQRT", 1, Type.FLOATING_TYPES), - new VOP("SSUB", 2, Type.INTEGRAL_TYPES), - new VOP("SUADD", 2, Type.INTEGRAL_TYPES), - new VOP("SUB", 2, Type.PRIMITIVE_TYPES), - new VOP("SUSUB", 2, Type.INTEGRAL_TYPES), - //new VOP("TAN", 1, Type.FLOATING_TYPES), - //new VOP("TANH", 1, Type.FLOATING_TYPES), - new VOP("TRAILING_ZEROS_COUNT", 1, Type.INTEGRAL_TYPES), - new VOP("UMAX", 2, Type.INTEGRAL_TYPES), - new VOP("UMIN", 2, Type.INTEGRAL_TYPES), - new VOP("XOR", 2, Type.INTEGRAL_TYPES), - new VOP("ZOMO", 1, Type.INTEGRAL_TYPES) + new VOP("ABS", VOPType.UNARY, Type.PRIMITIVE_TYPES), + //new VOP("ACOS", VOPType.UNARY, Type.FLOATING_TYPES), + new VOP("ADD", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("AND", VOPType.ASSOCIATIVE, Type.INTEGRAL_TYPES), + new VOP("AND_NOT", VOPType.BINARY, Type.INTEGRAL_TYPES), + new VOP("ASHR", VOPType.BINARY, Type.INTEGRAL_TYPES), + //new VOP("ASIN", VOPType.UNARY, Type.FLOATING_TYPES), + //new VOP("ATAN", VOPType.UNARY, Type.FLOATING_TYPES), + //new VOP("ATAN2", VOPType.BINARY, Type.FLOATING_TYPES), + new VOP("BIT_COUNT", VOPType.UNARY, Type.INTEGRAL_TYPES), + new VOP("BITWISE_BLEND", VOPType.TERNARY, Type.INTEGRAL_TYPES), + //new VOP("CBRT", VOPType.UNARY, Type.FLOATING_TYPES), + new VOP("COMPRESS_BITS", VOPType.BINARY, Type.INT_LONG_TYPES), + //new VOP("COS", VOPType.UNARY, Type.FLOATING_TYPES), + //new VOP("COSH", VOPType.UNARY, Type.FLOATING_TYPES), + new VOP("DIV", VOPType.BINARY, Type.FLOATING_TYPES), + //new VOP("EXP", VOPType.UNARY, Type.FLOATING_TYPES), + new VOP("EXPAND_BITS", VOPType.BINARY, Type.INT_LONG_TYPES), + //new VOP("EXPM1", VOPType.UNARY, Type.FLOATING_TYPES), + new VOP("FIRST_NONZERO", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("FMA", VOPType.TERNARY, Type.FLOATING_TYPES), + //new VOP("HYPOT", VOPType.BINARY, Type.FLOATING_TYPES), + new VOP("LEADING_ZEROS_COUNT", VOPType.UNARY, Type.INTEGRAL_TYPES), + //new VOP("LOG", VOPType.UNARY, Type.FLOATING_TYPES), + //new VOP("LOG10", VOPType.UNARY, Type.FLOATING_TYPES), + //new VOP("LOG1P", VOPType.UNARY, Type.FLOATING_TYPES), + new VOP("LSHL", VOPType.BINARY, Type.INTEGRAL_TYPES), + new VOP("LSHR", VOPType.BINARY, Type.INTEGRAL_TYPES), + new VOP("MIN", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("MAX", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("MUL", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("NEG", VOPType.UNARY, Type.PRIMITIVE_TYPES), + new VOP("NOT", VOPType.UNARY, Type.INTEGRAL_TYPES), + new VOP("OR", VOPType.ASSOCIATIVE, Type.INTEGRAL_TYPES), + //new VOP("POW", VOPType.BINARY, Type.FLOATING_TYPES), + new VOP("REVERSE", VOPType.UNARY, Type.INTEGRAL_TYPES), + new VOP("REVERSE_BYTES", VOPType.UNARY, Type.INTEGRAL_TYPES), + new VOP("ROL", VOPType.BINARY, Type.INTEGRAL_TYPES), + new VOP("ROR", VOPType.BINARY, Type.INTEGRAL_TYPES), + new VOP("SADD", VOPType.BINARY, Type.INTEGRAL_TYPES), + //new VOP("SIN", VOPType.UNARY, Type.FLOATING_TYPES), + //new VOP("SINH", VOPType.UNARY, Type.FLOATING_TYPES), + //new VOP("SQRT", VOPType.UNARY, Type.FLOATING_TYPES), + new VOP("SSUB", VOPType.BINARY, Type.INTEGRAL_TYPES), + new VOP("SUADD", VOPType.BINARY, Type.INTEGRAL_TYPES), + new VOP("SUB", VOPType.BINARY, Type.PRIMITIVE_TYPES), + new VOP("SUSUB", VOPType.BINARY, Type.INTEGRAL_TYPES), + //new VOP("TAN", VOPType.UNARY, Type.FLOATING_TYPES), + //new VOP("TANH", VOPType.UNARY, Type.FLOATING_TYPES), + new VOP("TRAILING_ZEROS_COUNT", VOPType.UNARY, Type.INTEGRAL_TYPES), + new VOP("UMAX", VOPType.ASSOCIATIVE, Type.INTEGRAL_TYPES), + new VOP("UMIN", VOPType.ASSOCIATIVE, Type.INTEGRAL_TYPES), + new VOP("XOR", VOPType.ASSOCIATIVE, Type.INTEGRAL_TYPES), + new VOP("ZOMO", VOPType.UNARY, Type.INTEGRAL_TYPES) ); private static final List generateVectorAPIOperations() { @@ -454,27 +454,32 @@ private static final List generateVectorAPIOperations() { ops.add(new Operation.Binary(type.elementType, "", type, ".lane(", Type.ints(), " & " + (type.length-1) + ")", null)); for (VOP vop : VECTOR_API_OPS) { - if (vop.args() == 1 && vop.elementTypes().contains(type.elementType)) { - ops.add(new Operation.Unary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ")", null)); - // TODO: lanewise(VectorOperators.Unary op, VectorMask m) - } - if (vop.args() == 2 && vop.elementTypes().contains(type.elementType)) { - ops.add(new Operation.Binary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ")", null)); - // TODO: lanewise(VectorOperators.Binary op, int e, VectorMask m) - // TODO: lanewise(VectorOperators.Binary op, long e) - // TODO: lanewise(VectorOperators.Binary op, long e, VectorMask m) - ops.add(new Operation.Binary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ")", null)); - // TODO: lanewise(VectorOperators.Binary op, Vector v, VectorMask m) - } - if (vop.args() == 3 && vop.elementTypes().contains(type.elementType)) { - ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type.elementType, ")", null)); - // TODO: lanewise(VectorOperators.Ternary op, int e1, int e2, VectorMask m) - ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type, ")", null)); - // TODO: lanewise(VectorOperators.Ternary op, int e1, Vector v2, VectorMask m) - ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ", ", type.elementType, ")", null)); - // TODO: lanewise(VectorOperators.Ternary op, Vector v1, int e2, VectorMask m) - ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ", ", type, ")", null)); - // TODO: lanewise(VectorOperators.Ternary op, Vector v1, Vector v2, VectorMask m) + if (vop.elementTypes().contains(type.elementType)) { + switch(vop.type()) { + case VOPType.UNARY: + ops.add(new Operation.Unary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ")", null)); + // TODO: lanewise(VectorOperators.Unary op, VectorMask m) + break; + case VOPType.BINARY: + case VOPType.ASSOCIATIVE: + ops.add(new Operation.Binary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ")", null)); + // TODO: lanewise(VectorOperators.Binary op, int e, VectorMask m) + // TODO: lanewise(VectorOperators.Binary op, long e) + // TODO: lanewise(VectorOperators.Binary op, long e, VectorMask m) + ops.add(new Operation.Binary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ")", null)); + // TODO: lanewise(VectorOperators.Binary op, Vector v, VectorMask m) + break; + case VOPType.TERNARY: + ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type.elementType, ")", null)); + // TODO: lanewise(VectorOperators.Ternary op, int e1, int e2, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type, ")", null)); + // TODO: lanewise(VectorOperators.Ternary op, int e1, Vector v2, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ", ", type.elementType, ")", null)); + // TODO: lanewise(VectorOperators.Ternary op, Vector v1, int e2, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ", ", type, ")", null)); + // TODO: lanewise(VectorOperators.Ternary op, Vector v1, Vector v2, VectorMask m) + break; + } } } @@ -495,6 +500,7 @@ private static final List generateVectorAPIOperations() { // TODO: rearrange(VectorShuffle shuffle) // TODO: rearrange(VectorShuffle s, Vector v) // TODO: rearrange(VectorShuffle s, VectorMask m) + } // Ensure the list is immutable. diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java index 71be117c16e1e..1b4daf2d72390 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java @@ -212,6 +212,9 @@ public static String generate(CompileFramework comp) { ); }); + // TODO: add register stress test + // TODO: add load/store stress test + // Now use the templates and add them into the IRTestClass. List templates = new ArrayList<>(); templates.add(Library.arrayFillMethods()); From 88def19f038cedaaa12f96824a7e524e16abe755 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 6 Mar 2025 16:41:34 +0100 Subject: [PATCH 153/212] reduction --- .../jtreg/compiler/lib/template_library/Operations.java | 5 ++++- test/hotspot/jtreg/compiler/lib/verify/Verify.java | 8 ++++---- .../template_library/examples/TestFuzzVectorAPI.java | 1 + 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 3b99c1a42fc8e..4aca180cbb3b1 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -460,8 +460,11 @@ private static final List generateVectorAPIOperations() { ops.add(new Operation.Unary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ")", null)); // TODO: lanewise(VectorOperators.Unary op, VectorMask m) break; - case VOPType.BINARY: case VOPType.ASSOCIATIVE: + ops.add(new Operation.Unary(type.elementType, "", type, ".reduceLanes(VectorOperators." + vop.name() + ")", null)); + // TODO: reduceLanes(VectorOperators.Associative op, VectorMask m) + // fall-through + case VOPType.BINARY: ops.add(new Operation.Binary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ")", null)); // TODO: lanewise(VectorOperators.Binary op, int e, VectorMask m) // TODO: lanewise(VectorOperators.Binary op, long e) diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index 4764a8340b00a..4d5090246b8bf 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -300,7 +300,7 @@ private static void checkEQimpl(float[] a, float[] b, String context) { for (int i = 0; i < a.length; i++) { if (Float.floatToIntBits(a[i]) != Float.floatToIntBits(b[i])) { System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i] + " " + context); - throw new VerifyException("Float array value mismatch."); + throw new VerifyException("Float array value mismatch " + a[i] + " vs " + b[i]); } } } @@ -318,7 +318,7 @@ private static void checkEQimpl(double[] a, double[] b, String context) { for (int i = 0; i < a.length; i++) { if (Double.doubleToLongBits(a[i]) != Double.doubleToLongBits(b[i])) { System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i] + " " + context); - throw new VerifyException("Double array value mismatch."); + throw new VerifyException("Double array value mismatch " + a[i] + " vs " + b[i]); } } } @@ -408,7 +408,7 @@ private static void checkEQimpl(FloatVector a, FloatVector b, String context) { for (int i = 0; i < a.length(); i++) { if (Float.floatToIntBits(a.lane(i)) != Float.floatToIntBits(b.lane(i))) { System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a.lane(i) + " vs " + b.lane(i) + " " + context); - throw new VerifyException("FloatVector value mismatch."); + throw new VerifyException("FloatVector value mismatch " + a.lane(i) + " vs " + b.lane(i)); } } } @@ -426,7 +426,7 @@ private static void checkEQimpl(DoubleVector a, DoubleVector b, String context) for (int i = 0; i < a.length(); i++) { if (Double.doubleToLongBits(a.lane(i)) != Double.doubleToLongBits(b.lane(i))) { System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a.lane(i) + " vs " + b.lane(i) + " " + context); - throw new VerifyException("DoubleVector value mismatch."); + throw new VerifyException("DoubleVector value mismatch " + a.lane(i) + " vs " + b.lane(i)); } } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java index 1b4daf2d72390..eded4e57965bc 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java @@ -212,6 +212,7 @@ public static String generate(CompileFramework comp) { ); }); + // TODO: add scalar output case for reductions // TODO: add register stress test // TODO: add load/store stress test From 3d0e62c956432c5b7c89465d33f799758c1f40d5 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 6 Mar 2025 16:56:52 +0100 Subject: [PATCH 154/212] restrict some operation generations further --- .../lib/template_library/Operations.java | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 4aca180cbb3b1..51ea93c279ea4 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -311,14 +311,20 @@ final class Operations { BOOLEAN_OPERATIONS ).flatMap((List l) -> l.stream()).toList(); - private enum VOPType { UNARY, BINARY, ASSOCIATIVE, TERNARY } + private enum VOPType { + UNARY, + BINARY, + ASSOCIATIVE, // Binary and associative - safe for reductions of any type + INTEGRAL_ASSOCIATIVE, // Binary - but only safe for integral reductions + TERNARY + } private record VOP(String name, VOPType type, List elementTypes) {} // TODO: consider some floating results as inexact, and handle it accordingly? private static final List VECTOR_API_OPS = List.of( new VOP("ABS", VOPType.UNARY, Type.PRIMITIVE_TYPES), //new VOP("ACOS", VOPType.UNARY, Type.FLOATING_TYPES), - new VOP("ADD", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("ADD", VOPType.INTEGRAL_ASSOCIATIVE, Type.PRIMITIVE_TYPES), new VOP("AND", VOPType.ASSOCIATIVE, Type.INTEGRAL_TYPES), new VOP("AND_NOT", VOPType.BINARY, Type.INTEGRAL_TYPES), new VOP("ASHR", VOPType.BINARY, Type.INTEGRAL_TYPES), @@ -346,7 +352,7 @@ private record VOP(String name, VOPType type, List elementTypes) new VOP("LSHR", VOPType.BINARY, Type.INTEGRAL_TYPES), new VOP("MIN", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), new VOP("MAX", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), - new VOP("MUL", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("MUL", VOPType.INTEGRAL_ASSOCIATIVE, Type.PRIMITIVE_TYPES), new VOP("NEG", VOPType.UNARY, Type.PRIMITIVE_TYPES), new VOP("NOT", VOPType.UNARY, Type.INTEGRAL_TYPES), new VOP("OR", VOPType.ASSOCIATIVE, Type.INTEGRAL_TYPES), @@ -426,13 +432,16 @@ private static final List generateVectorAPIOperations() { + type2.elementType.name() + ".class, " + type.elementType.name() + ".class), 0))", null)); - ops.add(new Operation.Unary(type, - "((" + type.vectorType + ")", - type2 , - ".convert(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), 0))", - null)); + // Reinterpretation FROM floating is not safe, because of different NaN encodings. + if (!type2.elementType.isFloating()) { + ops.add(new Operation.Unary(type, + "((" + type.vectorType + ")", + type2 , + ".convert(VectorOperators.Conversion.ofReinterpret(" + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), 0))", + null)); + } } // TODO: convertShape @@ -461,8 +470,11 @@ private static final List generateVectorAPIOperations() { // TODO: lanewise(VectorOperators.Unary op, VectorMask m) break; case VOPType.ASSOCIATIVE: - ops.add(new Operation.Unary(type.elementType, "", type, ".reduceLanes(VectorOperators." + vop.name() + ")", null)); - // TODO: reduceLanes(VectorOperators.Associative op, VectorMask m) + case VOPType.INTEGRAL_ASSOCIATIVE: + if (vop.type() == VOPType.ASSOCIATIVE || !type.elementType.isFloating()) { + ops.add(new Operation.Unary(type.elementType, "", type, ".reduceLanes(VectorOperators." + vop.name() + ")", null)); + // TODO: reduceLanes(VectorOperators.Associative op, VectorMask m) + } // fall-through case VOPType.BINARY: ops.add(new Operation.Binary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ")", null)); @@ -504,6 +516,7 @@ private static final List generateVectorAPIOperations() { // TODO: rearrange(VectorShuffle s, Vector v) // TODO: rearrange(VectorShuffle s, VectorMask m) + // FIXME: continue with: reinterpretAsBytes() } // Ensure the list is immutable. From 7380f19256fa04aac3e3d92dc62143884b455ef9 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 6 Mar 2025 17:14:28 +0100 Subject: [PATCH 155/212] more reinterpret --- .../lib/template_library/Operations.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 51ea93c279ea4..5eb1b97c2c1c8 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -441,10 +441,28 @@ private static final List generateVectorAPIOperations() { + type2.elementType.name() + ".class, " + type.elementType.name() + ".class), 0))", null)); + if (type.elementType == Type.bytes()) { + ops.add(new Operation.Unary(type, "", type2, ".reinterpretAsBytes()", null)); + } + if (type.elementType == Type.shorts()) { + ops.add(new Operation.Unary(type, "", type2, ".reinterpretAsShorts()", null)); + } + if (type.elementType == Type.ints()) { + ops.add(new Operation.Unary(type, "", type2, ".reinterpretAsInts()", null)); + } + if (type.elementType == Type.longs()) { + ops.add(new Operation.Unary(type, "", type2, ".reinterpretAsLongs()", null)); + } + if (type.elementType == Type.floats()) { + ops.add(new Operation.Unary(type, "", type2, ".reinterpretAsFloats()", null)); + } + if (type.elementType == Type.doubles()) { + ops.add(new Operation.Unary(type, "", type2, ".reinterpretAsDoubles()", null)); + } } } - // TODO: convertShape + // TODO: reinterpretShape } ops.add(new Operation.Binary(type, "", type, ".div(", type.elementType, ")", List.of("ArithmeticException"))); @@ -516,7 +534,7 @@ private static final List generateVectorAPIOperations() { // TODO: rearrange(VectorShuffle s, Vector v) // TODO: rearrange(VectorShuffle s, VectorMask m) - // FIXME: continue with: reinterpretAsBytes() + // FIXME: continue with: selectFrom } // Ensure the list is immutable. From e1d7442bd4826bd963af4155c0f756ff4f7a7bb5 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 6 Mar 2025 17:24:27 +0100 Subject: [PATCH 156/212] selectFrom --- .../jtreg/compiler/lib/template_library/Operations.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 5eb1b97c2c1c8..d46c27d393722 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -534,7 +534,9 @@ private static final List generateVectorAPIOperations() { // TODO: rearrange(VectorShuffle s, Vector v) // TODO: rearrange(VectorShuffle s, VectorMask m) - // FIXME: continue with: selectFrom + ops.add(new Operation.Binary(type, "", type, ".selectFrom(", type, ")", null)); + ops.add(new Operation.Ternary(type, "", type, ".selectFrom(", type, ", ", type, ")", null)); + // TODO: selectFrom(Vector s, VectorMask m) } // Ensure the list is immutable. From 1fec66b710686f715bfc751aed285c93499bbc8f Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Mar 2025 08:11:49 +0100 Subject: [PATCH 157/212] slice and some other fixes --- .../jtreg/compiler/lib/template_library/Expression.java | 2 +- .../jtreg/compiler/lib/template_library/Operations.java | 5 +++++ .../template_library/examples/TestFuzzVectorAPI.java | 9 +++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index 1b03f22333282..dafa176a4211d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -156,7 +156,7 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result if (es != null) { exceptions.addAll(es); } ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, argTypes, exceptions); ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, ops, maxDepth-1, argTypes, exceptions); - ExpressionGeneratorStep step2 = expressionGeneratorStep(t1, ops, maxDepth-1, argTypes, exceptions); + ExpressionGeneratorStep step2 = expressionGeneratorStep(t2, ops, maxDepth-1, argTypes, exceptions); return (List tokens, List args) -> { tokens.add(s0); step0.addTokens(tokens, args); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index d46c27d393722..3fe9fa75f3462 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -537,6 +537,11 @@ private static final List generateVectorAPIOperations() { ops.add(new Operation.Binary(type, "", type, ".selectFrom(", type, ")", null)); ops.add(new Operation.Ternary(type, "", type, ".selectFrom(", type, ", ", type, ")", null)); // TODO: selectFrom(Vector s, VectorMask m) + + ops.add(new Operation.Binary(type, "", type, ".slice(", Type.ints(), ")", List.of("IndexOutOfBoundsException"))); + ops.add(new Operation.Ternary(type, "", type, ".slice(", Type.ints(), ", ", type, ")", List.of("IndexOutOfBoundsException"))); + // TODO: slice(int origin, Vector w, VectorMask m) + } // Ensure the list is immutable. diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java index eded4e57965bc..320a3ec31b55b 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java @@ -98,9 +98,12 @@ public static String generate(CompileFramework comp) { try { """, " return ", expression.withRandomArgs(), ";\n", + expression.exceptions().stream().map(exception -> + "} catch (" + exception + " e) { return e;\n" + ).toList(), """ - } catch (Exception e) { - return e; + } finally { + // Just javac is happy if there are no exceptions to catch. } """ ); @@ -162,6 +165,8 @@ public static String generate(CompileFramework comp) { } else if (argType instanceof VectorAPIType vt) { elementType = vt.elementType; args.add(vt.vectorType + ".fromArray(" + vt.species + ", " + name + ", 0)"); + } else { + throw new RuntimeException("Not handled: " + argType); } arrayDefinitions.add(defineArray.withArgs(elementType, name, size)); } From e83b7565ba51ee46edf4c738e41b9f3ea448ef04 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Mar 2025 08:27:32 +0100 Subject: [PATCH 158/212] more ops --- .../lib/template_library/Operations.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 3fe9fa75f3462..b752a185038d5 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -459,6 +459,12 @@ private static final List generateVectorAPIOperations() { if (type.elementType == Type.doubles()) { ops.add(new Operation.Unary(type, "", type2, ".reinterpretAsDoubles()", null)); } + if (type.elementType.isFloating() && type.elementType.sizeInBits() == type2.elementType.sizeInBits()) { + ops.add(new Operation.Unary(type, "", type2, ".viewAsFloatingLanes()", null)); + } + if (!type.elementType.isFloating() && type.elementType.sizeInBits() == type2.elementType.sizeInBits()) { + ops.add(new Operation.Unary(type, "", type2, ".viewAsIntegralLanes()", null)); + } } } // TODO: convertShape @@ -475,7 +481,7 @@ private static final List generateVectorAPIOperations() { // TODO: expand(VectorMask m) - // TODO: ensure we use all variants of fromArray and fromMemorySegment, plus intoArray and intoMemorySegment. + // TODO: ensure we use all variants of fromArray and fromMemorySegment, plus intoArray and intoMemorySegment. Also: toArray and type variants. // TODO: lane case that is allowed to throw java.lang.IllegalArgumentException for out of bonds. ops.add(new Operation.Binary(type.elementType, "", type, ".lane(", Type.ints(), " & " + (type.length-1) + ")", null)); @@ -542,6 +548,19 @@ private static final List generateVectorAPIOperations() { ops.add(new Operation.Ternary(type, "", type, ".slice(", Type.ints(), ", ", type, ")", List.of("IndexOutOfBoundsException"))); // TODO: slice(int origin, Vector w, VectorMask m) + ops.add(new Operation.Binary(type, "", type, ".sub(", type.elementType, ")", null)); + // TODO: sub(int e, VectorMask m) + ops.add(new Operation.Binary(type, "", type, ".sub(", type, ")", null)); + // TODO: sub(Vector v, VectorMask m) + + // TODO: test(VectorOperators.Test op) + // TODO: test(VectorOperators.Test op, VectorMask m) + + ops.add(new Operation.Binary(type, "", type, ".unslice(", Type.ints(), ")", List.of("IndexOutOfBoundsException"))); + // TODO: unslice(int origin, Vector w, int part) + // TODO: unslice(int origin, Vector w, int part, VectorMask m) + + // FIXME: withLane } // Ensure the list is immutable. From 0c1c110c98bde75ebf50ea3d8e75fd06b15a35e2 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Mar 2025 08:38:07 +0100 Subject: [PATCH 159/212] withLane --- .../hotspot/jtreg/compiler/lib/template_library/Operations.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index b752a185038d5..3c5fa8b08c7b8 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -560,7 +560,7 @@ private static final List generateVectorAPIOperations() { // TODO: unslice(int origin, Vector w, int part) // TODO: unslice(int origin, Vector w, int part, VectorMask m) - // FIXME: withLane + ops.add(new Operation.Ternary(type, "", type, ".withLane(", Type.ints(), ", ", type.elementType, ")", List.of("IllegalArgumentException"))); } // Ensure the list is immutable. From 9a326244f5263c584f01785b1a8e9b9f8afab1f3 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Mar 2025 09:01:27 +0100 Subject: [PATCH 160/212] more ops --- .../compiler/lib/template_library/Operations.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 3c5fa8b08c7b8..7700d0b550f0e 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -561,6 +561,16 @@ private static final List generateVectorAPIOperations() { // TODO: unslice(int origin, Vector w, int part, VectorMask m) ops.add(new Operation.Ternary(type, "", type, ".withLane(", Type.ints(), ", ", type.elementType, ")", List.of("IllegalArgumentException"))); + + if (type.elementType.isFloating()) { + ops.add(new Operation.Ternary(type, "", type, ".fma(", type.elementType, ", ", type.elementType, ")", null)); + ops.add(new Operation.Ternary(type, "", type, ".fma(", type, ", ", type, ")", null)); + + // TODO: precision? + // ops.add(new Operation.Binary(type, "", type, ".pow(", type.elementType, ")", null)); + // ops.add(new Operation.Binary(type, "", type, ".pow(", type, ")", null)); + // ops.add(new Operation.Unary(type, "", type, ".sqrt(", type, ")", null)); + } } // Ensure the list is immutable. From 443676e29f910828dcaf872f81531727a81a0561 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Mar 2025 09:32:43 +0100 Subject: [PATCH 161/212] note about toShuffle --- .../hotspot/jtreg/compiler/lib/template_library/Operations.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 7700d0b550f0e..3f35b54b6c818 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -571,6 +571,8 @@ private static final List generateVectorAPIOperations() { // ops.add(new Operation.Binary(type, "", type, ".pow(", type, ")", null)); // ops.add(new Operation.Unary(type, "", type, ".sqrt(", type, ")", null)); } + + // TODO: toShuffle - VectorShuffle } // Ensure the list is immutable. From c812aae0091fb886cf6f2f7196bed5086737b1ed Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 7 Mar 2025 13:19:43 +0100 Subject: [PATCH 162/212] wip MaskType and Verify removed explicit dependency on VectorAPI --- .../lib/template_library/VectorAPIType.java | 32 +++- .../jtreg/compiler/lib/verify/Verify.java | 138 +++--------------- 2 files changed, 54 insertions(+), 116 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java index 37ec508ba3256..9800a124a7a2a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java @@ -29,7 +29,7 @@ import compiler.lib.template_framework.Name; -public class VectorAPIType extends Type { +public final class VectorAPIType extends Type { private static final Random RANDOM = Utils.getRandomInstance(); public static final VectorAPIType BYTE_64 = new VectorAPIType(Type.bytes(), 8); @@ -67,11 +67,14 @@ public class VectorAPIType extends Type { public final String vectorType; public final String species; + public final MaskType maskType; + private VectorAPIType(PrimitiveType elementType, int length) { this.elementType = elementType; this.length = length; this.vectorType = elementType.vectorAPITypeName(); this.species = vectorType + ".SPECIES_" + (elementType.sizeInBits() * length); + this.maskType = new MaskType(this); } @Override @@ -91,4 +94,31 @@ public final Object con() { } public final int sizeInBits() { return length * elementType.sizeInBits(); } + + public final class MaskType extends Type { + public final VectorAPIType vectorType; + public final String vectorMaskTypeName; + + MaskType(VectorAPIType vectorType) { + this.vectorType = vectorType; + this.vectorMaskTypeName = "VectorMask<" + vectorType.elementType.boxedTypeName() + ">"; + } + + @Override + public boolean isSubtypeOf(Name.Type other) { + // TODO: re-evaluate + return this == other; + } + + @Override + public final String name() { return vectorMaskTypeName; } + + @Override + public final Object con() { + // TODO: + return vectorMaskTypeName + ".allTrue()"; + } + + public final int sizeInBits() { return vectorType.sizeInBits(); } + } } diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index 4d5090246b8bf..c18f5525af7cc 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -25,7 +25,8 @@ import java.util.Optional; import java.lang.foreign.*; -import jdk.incubator.vector.*; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; /** * The {@link Verify} class provides a single {@link Verify#checkEQ} static method, which recursively @@ -98,14 +99,29 @@ private static void checkEQ(Object a, Object b, String context) { case double[] x -> checkEQimpl(x, (double[])b, context); case boolean[] x -> checkEQimpl(x, (boolean[])b, context); case MemorySegment x -> checkEQimpl(x, (MemorySegment) b, context); - case ByteVector x -> checkEQimpl(x, (ByteVector) b, context); - case ShortVector x -> checkEQimpl(x, (ShortVector) b, context); - case IntVector x -> checkEQimpl(x, (IntVector) b, context); - case LongVector x -> checkEQimpl(x, (LongVector) b, context); - case FloatVector x -> checkEQimpl(x, (FloatVector) b, context); - case DoubleVector x -> checkEQimpl(x, (DoubleVector) b, context); case Exception x -> checkEQimpl(x, (Exception) b, context); default -> { + if (ca.getName().startsWith("jdk.incubator.vector") && ca.getName().contains("Vector")) { + // We do not want to import jdk.incubator.vector explicitly, because it would mean we would also have + // to add "--add-modules=jdk.incubator.vector" to the command-line of every test that uses the Verify + // class. So we hack this via reflection. + Object va = null; + Object vb = null; + try { + Method m = ca.getMethod("toArray"); + va = m.invoke(a); + vb = m.invoke(b); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Could not invoke toArray on " + ca.getName(), e); + } catch (IllegalAccessException e) { + throw new RuntimeException("Could not invoke toArray on " + ca.getName(), e); + } catch (InvocationTargetException e) { + throw new RuntimeException("Could not invoke toArray on " + ca.getName(), e); + } + checkEQ(va, vb, context); + return; + } + System.err.println("ERROR: Verify.checkEQ failed: type not supported: " + ca.getName()); print(a, "a " + context); print(b, "b " + context); @@ -340,114 +356,6 @@ private static void checkEQimpl(boolean[] a, boolean[] b, String context) { } } - /** - * Verify that the content of two ShortVector is identical. - */ - private static void checkEQimpl(ShortVector a, ShortVector b, String context) { - if (a.length() != b.length()) { - System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length() + " vs " + b.length() + " " + context); - throw new VerifyException("ShortVector length mismatch."); - } - - for (int i = 0; i < a.length(); i++) { - if (a.lane(i) != b.lane(i)) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a.lane(i) + " vs " + b.lane(i) + " " + context); - throw new VerifyException("ShortVector value mismatch."); - } - } - } - - /** - * Verify that the content of two IntVector is identical. - */ - private static void checkEQimpl(IntVector a, IntVector b, String context) { - if (a.length() != b.length()) { - System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length() + " vs " + b.length() + " " + context); - throw new VerifyException("IntVector length mismatch."); - } - - for (int i = 0; i < a.length(); i++) { - if (a.lane(i) != b.lane(i)) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a.lane(i) + " vs " + b.lane(i) + " " + context); - throw new VerifyException("IntVector value mismatch."); - } - } - } - - /** - * Verify that the content of two ByteVector is identical. - */ - private static void checkEQimpl(LongVector a, LongVector b, String context) { - if (a.length() != b.length()) { - System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length() + " vs " + b.length() + " " + context); - throw new VerifyException("LongVector length mismatch."); - } - - for (int i = 0; i < a.length(); i++) { - if (a.lane(i) != b.lane(i)) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a.lane(i) + " vs " + b.lane(i) + " " + context); - throw new VerifyException("LongVector value mismatch."); - } - } - } - - /** - * Verify that the content of two ByteVector is identical. - * Ideally, we would want to assert that the Float.floatToRawIntBits are identical. - * But the Java spec allows us to return different bits for a NaN, which allows swapping the inputs - * of an add or mul (NaN1 * NaN2 does not have same bits as NaN2 * NaN1, because the multiplication - * of two NaN values should always return the first of the two). So we verify that we have the same bit - * pattern in all cases, except for NaN we project to the canonical NaN, using Float.floatToIntBits. - */ - private static void checkEQimpl(FloatVector a, FloatVector b, String context) { - if (a.length() != b.length()) { - System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length() + " vs " + b.length() + " " + context); - throw new VerifyException("FloatVector length mismatch."); - } - - for (int i = 0; i < a.length(); i++) { - if (Float.floatToIntBits(a.lane(i)) != Float.floatToIntBits(b.lane(i))) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a.lane(i) + " vs " + b.lane(i) + " " + context); - throw new VerifyException("FloatVector value mismatch " + a.lane(i) + " vs " + b.lane(i)); - } - } - } - - /** - * Verify that the content of two ByteVector is identical. - * Same issue with NaN as above for floats. - */ - private static void checkEQimpl(DoubleVector a, DoubleVector b, String context) { - if (a.length() != b.length()) { - System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length() + " vs " + b.length() + " " + context); - throw new VerifyException("DoubleVector length mismatch."); - } - - for (int i = 0; i < a.length(); i++) { - if (Double.doubleToLongBits(a.lane(i)) != Double.doubleToLongBits(b.lane(i))) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a.lane(i) + " vs " + b.lane(i) + " " + context); - throw new VerifyException("DoubleVector value mismatch " + a.lane(i) + " vs " + b.lane(i)); - } - } - } - - /** - * Verify that the content of two ByteVector is identical. - */ - private static void checkEQimpl(ByteVector a, ByteVector b, String context) { - if (a.length() != b.length()) { - System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length() + " vs " + b.length() + " " + context); - throw new VerifyException("ByteVector length mismatch."); - } - - for (int i = 0; i < a.length(); i++) { - if (a.lane(i) != b.lane(i)) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a.lane(i) + " vs " + b.lane(i) + " " + context); - throw new VerifyException("ByteVector value mismatch."); - } - } - } - /** * Verify that the content of two Object arrays is identical, recursively: * every element is compared with checkEQimpl for the corresponding type. From c4340f2b9c58443696ce7d3c7753f189589d81e2 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Mar 2025 15:49:22 +0100 Subject: [PATCH 163/212] compile and vmFlags --- .../compiler/lib/compile_framework/Compile.java | 13 +++++++++---- .../lib/compile_framework/CompileFramework.java | 4 ++-- .../compiler/lib/template_library/IRTestClass.java | 8 ++++++-- .../examples/TestFuzzVectorAPI.java | 6 ++++-- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java b/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java index 0f45d982af6d6..f7b0d4c375440 100644 --- a/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java @@ -30,6 +30,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.concurrent.TimeUnit; import java.util.List; import jdk.test.lib.JDKToolFinder; @@ -47,7 +48,7 @@ class Compile { * Compile all sources in {@code javaSources}. First write them to the {@code sourceDir}, * then compile them to class-files which are stored in {@code classesDir}. */ - public static void compileJavaSources(List javaSources, Path sourceDir, Path classesDir) { + public static void compileJavaSources(List javaSources, Path sourceDir, Path classesDir, String[] javacFlags) { if (javaSources.isEmpty()) { Utils.printlnVerbose("No java sources to compile."); return; @@ -55,7 +56,7 @@ public static void compileJavaSources(List javaSources, Path sourceD Utils.printlnVerbose("Compiling Java sources: " + javaSources.size()); List javaFilePaths = writeSourcesToFiles(javaSources, sourceDir); - compileJavaFiles(javaFilePaths, classesDir); + compileJavaFiles(javaFilePaths, classesDir, javacFlags); Utils.printlnVerbose("Java sources compiled."); } @@ -63,10 +64,13 @@ public static void compileJavaSources(List javaSources, Path sourceD * Compile a list of files (i.e. {@code paths}) using javac and store * them in {@code classesDir}. */ - private static void compileJavaFiles(List paths, Path classesDir) { + private static void compileJavaFiles(List paths, Path classesDir, String[] javacFlags) { List command = new ArrayList<>(); command.add(JAVAC_PATH); + if (javacFlags != null) { + command.addAll(Arrays.asList(javacFlags)); + } command.add("-classpath"); // Note: the backslashes from windows paths must be escaped! command.add(Utils.getEscapedClassPathAndClassesDir(classesDir)); @@ -192,8 +196,9 @@ private static void executeCompileCommand(List command) { throw new CompileFrameworkException("InterruptedException during compilation", e); } - if (exitCode != 0 || !output.isEmpty()) { + if (exitCode != 0) { System.err.println("Compilation failed."); + System.err.println("Command: " + command); System.err.println("Exit code: " + exitCode); System.err.println("Output: '" + output + "'"); throw new CompileFrameworkException("Compilation failed."); diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java b/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java index fe23d596f3c64..8304a5f989cf0 100644 --- a/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java @@ -72,7 +72,7 @@ public void addJasmSourceCode(String className, String code) { * Java and Jasm sources and store the generated class-files in the {@link classesDir} * directory. */ - public void compile() { + public void compile(String... javacFlags) { if (classLoader != null) { throw new CompileFrameworkException("Cannot compile twice!"); } @@ -86,7 +86,7 @@ public void compile() { System.out.println("Classes directory: " + classesDir); Compile.compileJasmSources(jasmSources, sourceDir, classesDir); - Compile.compileJavaSources(javaSources, sourceDir, classesDir); + Compile.compileJavaSources(javaSources, sourceDir, classesDir, javacFlags); classLoader = ClassLoaderBuilder.build(classesDir); } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/IRTestClass.java b/test/hotspot/jtreg/compiler/lib/template_library/IRTestClass.java index ea7d451477962..64aa4198c95fe 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/IRTestClass.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/IRTestClass.java @@ -34,7 +34,7 @@ * */ public abstract class IRTestClass { - public record Info(String classpath, String packageName, String className, List imports) {}; + public record Info(String classpath, String packageName, String className, List imports, List vmFlags) {}; public static final Template.TwoArgs> TEMPLATE = Template.make("info", "templates", (Info info, List templates) -> body( @@ -61,7 +61,11 @@ public class #className { public static void main() { TestFramework framework = new TestFramework(#className.class); - framework.addFlags("-classpath", "#classpath"); + framework.addFlags("-classpath", "#classpath" + """, + info.vmFlags().stream().map(f -> ", \"" + f + "\"").toList(), + """ + ); framework.start(); } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java index 320a3ec31b55b..612a0739ea638 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java @@ -24,6 +24,7 @@ /* * @test * @summary Test the Template Library's expression generation for the Vector API. + * @modules jdk.incubator.vector * @modules java.base/jdk.internal.misc * @library /test/lib / * @compile ../../../compiler/lib/ir_framework/TestFramework.java @@ -67,7 +68,7 @@ public static void main(String[] args) { comp.addJavaSourceCode("p.xyz.InnerTest", generate(comp)); // Compile the source file. - comp.compile(); + comp.compile("--add-modules=jdk.incubator.vector"); // Object ret = p.xyz.InnterTest.main(); Object ret = comp.invoke("p.xyz.InnerTest", "main", new Object[] {}); @@ -82,7 +83,8 @@ public static String generate(CompileFramework comp) { "p.xyz", "InnerTest", List.of("compiler.lib.generators.*", "compiler.lib.verify.*", - "jdk.incubator.vector.*")); + "jdk.incubator.vector.*"), + List.of("--add-modules=jdk.incubator.vector")); // Example 1: // We only use the "expression" once, and so we can conveniently just run it with From 911d723799f455b6166236bf6a20434bb7f8964f Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 10 Mar 2025 15:58:03 +0100 Subject: [PATCH 164/212] fix tests --- .../template_library/examples/TestFuzzExpression.java | 3 ++- .../template_library/examples/TestIRTestClass.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index 0971c1702addc..749747d96de15 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -87,7 +87,8 @@ public static String generate(CompileFramework comp) { IRTestClass.Info info = new IRTestClass.Info(comp.getEscapedClassPathOfCompiledClasses(), "p.xyz", "InnerTest", List.of("compiler.lib.generators.*", - "compiler.lib.verify.*")); + "compiler.lib.verify.*"), + List.of()); // Example 1: // We use the "expression" twice: once in a reference method that runs in the interpreter, diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestIRTestClass.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestIRTestClass.java index 95edd145198d0..af7e90d931615 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestIRTestClass.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestIRTestClass.java @@ -81,7 +81,8 @@ public static String generate(CompileFramework comp) { IRTestClass.Info info = new IRTestClass.Info(comp.getEscapedClassPathOfCompiledClasses(), "p.xyz", "InnerTest", List.of("compiler.lib.generators.*", - "compiler.lib.verify.*")); + "compiler.lib.verify.*"), + List.of()); // We define a Test-Template: // - static fields for inputs: INPUT_A and INPUT_B From 8924d9d370c60a09620acb7daf6f0803f35b9f64 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 11 Mar 2025 10:22:16 +0100 Subject: [PATCH 165/212] rm instructions for JDK-8351627 and rename type lists --- .../lib/template_library/Operations.java | 13 ++++--- .../compiler/lib/template_library/Type.java | 36 +++++++++++-------- .../examples/TestFuzzVectorAPI.java | 6 ++-- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 3f35b54b6c818..c31fab41418dc 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -359,8 +359,11 @@ private record VOP(String name, VOPType type, List elementTypes) //new VOP("POW", VOPType.BINARY, Type.FLOATING_TYPES), new VOP("REVERSE", VOPType.UNARY, Type.INTEGRAL_TYPES), new VOP("REVERSE_BYTES", VOPType.UNARY, Type.INTEGRAL_TYPES), - new VOP("ROL", VOPType.BINARY, Type.INTEGRAL_TYPES), - new VOP("ROR", VOPType.BINARY, Type.INTEGRAL_TYPES), + + // TODO: add back in after fix of JDK-8351627 + // new VOP("ROL", VOPType.BINARY, Type.INTEGRAL_TYPES), + // new VOP("ROR", VOPType.BINARY, Type.INTEGRAL_TYPES), + new VOP("SADD", VOPType.BINARY, Type.INTEGRAL_TYPES), //new VOP("SIN", VOPType.UNARY, Type.FLOATING_TYPES), //new VOP("SINH", VOPType.UNARY, Type.FLOATING_TYPES), @@ -381,7 +384,7 @@ private record VOP(String name, VOPType type, List elementTypes) private static final List generateVectorAPIOperations() { List ops = new ArrayList(); - for (var type : Type.VECTOR_API_TYPES) { + for (var type : Type.VECTOR_API_VECTOR_TYPES) { ops.add(new Operation.Unary(type, "", type, ".abs()", null)); ops.add(new Operation.Binary(type, "", type, ".add(", type.elementType, ")", null)); // TODO: add(int e, VectorMask m) @@ -412,7 +415,7 @@ private static final List generateVectorAPIOperations() { ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", Type.longs(), ")", List.of("IllegalArgumentException"))); // TODO: non zero parts - for (var type2 : Type.VECTOR_API_TYPES) { + for (var type2 : Type.VECTOR_API_VECTOR_TYPES) { ops.add(new Operation.Unary(type, "((" + type.vectorType + ")", type2 , ".castShape(" + type.species + ", 0))", null)); } @@ -422,7 +425,7 @@ private static final List generateVectorAPIOperations() { // TODO: compress with VectorMask type // TODO: non zero parts - for (var type2 : Type.VECTOR_API_TYPES) { + for (var type2 : Type.VECTOR_API_VECTOR_TYPES) { // "convert" keeps the same shape, i.e. length of the vector in bits. if (type.sizeInBits() == type2.sizeInBits()) { ops.add(new Operation.Unary(type, diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Type.java b/test/hotspot/jtreg/compiler/lib/template_library/Type.java index 9941e12c8b31d..72632c5f3b842 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Type.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Type.java @@ -79,60 +79,68 @@ public abstract class Type implements Name.Type { doubles() ); - public static final List VECTOR_API_BYTE_TYPES = List.of( + public static final List VECTOR_API_BYTE_VECTOR_TYPES = List.of( VectorAPIType.BYTE_64, VectorAPIType.BYTE_128, VectorAPIType.BYTE_256, VectorAPIType.BYTE_512 ); - public static final List VECTOR_API_SHORT_TYPES = List.of( + public static final List VECTOR_API_SHORT_VECTOR_TYPES = List.of( VectorAPIType.SHORT_64, VectorAPIType.SHORT_128, VectorAPIType.SHORT_256, VectorAPIType.SHORT_512 ); - public static final List VECTOR_API_INT_TYPES = List.of( + public static final List VECTOR_API_INT_VECTOR_TYPES = List.of( VectorAPIType.INT_64, VectorAPIType.INT_128, VectorAPIType.INT_256, VectorAPIType.INT_512 ); - public static final List VECTOR_API_LONG_TYPES = List.of( + public static final List VECTOR_API_LONG_VECTOR_TYPES = List.of( VectorAPIType.LONG_64, VectorAPIType.LONG_128, VectorAPIType.LONG_256, VectorAPIType.LONG_512 ); - public static final List VECTOR_API_FLOAT_TYPES = List.of( + public static final List VECTOR_API_FLOAT_VECTOR_TYPES = List.of( VectorAPIType.FLOAT_64, VectorAPIType.FLOAT_128, VectorAPIType.FLOAT_256, VectorAPIType.FLOAT_512 ); - public static final List VECTOR_API_DOUBLE_TYPES = List.of( + public static final List VECTOR_API_DOUBLE_VECTOR_TYPES = List.of( VectorAPIType.DOUBLE_64, VectorAPIType.DOUBLE_128, VectorAPIType.DOUBLE_256, VectorAPIType.DOUBLE_512 ); - public static final List VECTOR_API_TYPES = Stream.of( - VECTOR_API_BYTE_TYPES, - VECTOR_API_SHORT_TYPES, - VECTOR_API_INT_TYPES, - VECTOR_API_LONG_TYPES, - VECTOR_API_FLOAT_TYPES, - VECTOR_API_DOUBLE_TYPES + public static final List VECTOR_API_VECTOR_TYPES = Stream.of( + VECTOR_API_BYTE_VECTOR_TYPES, + VECTOR_API_SHORT_VECTOR_TYPES, + VECTOR_API_INT_VECTOR_TYPES, + VECTOR_API_LONG_VECTOR_TYPES, + VECTOR_API_FLOAT_VECTOR_TYPES, + VECTOR_API_DOUBLE_VECTOR_TYPES ).flatMap((List l) -> l.stream()).toList(); + public static final List VECTOR_API_MASK_TYPES = + VECTOR_API_VECTOR_TYPES.stream().map(t -> t.maskType).toList(); + + public static final List ALL_VECTOR_API_TYPES = Library.concat( + VECTOR_API_VECTOR_TYPES, + VECTOR_API_MASK_TYPES + ); + public static final List ALL_BUILTIN_TYPES = Library.concat( PRIMITIVE_TYPES, - VECTOR_API_TYPES + ALL_VECTOR_API_TYPES ); /** diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java index 612a0739ea638..d08d5000e01d8 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java @@ -226,9 +226,9 @@ public static String generate(CompileFramework comp) { // Now use the templates and add them into the IRTestClass. List templates = new ArrayList<>(); templates.add(Library.arrayFillMethods()); - for (VectorAPIType type : Type.VECTOR_API_TYPES) { - for (int i = 0; i < 10; i++) { templates.add(template1.withArgs(type)); } - for (int i = 0; i < 10; i++) { templates.add(template2.withArgs(type)); } + for (VectorAPIType type : Type.VECTOR_API_VECTOR_TYPES) { + for (int i = 0; i < 2; i++) { templates.add(template1.withArgs(type)); } + for (int i = 0; i < 2; i++) { templates.add(template2.withArgs(type)); } } return IRTestClass.TEMPLATE.withArgs(info, templates).render(); } From c696d46d96a3062aceb4d1360f1ca8b61aae0f28 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 11 Mar 2025 11:23:31 +0100 Subject: [PATCH 166/212] some first masked ops and disabling ops from JDK-8350177 --- .../lib/template_library/Operations.java | 20 +++++++++++++------ .../lib/template_library/VectorAPIType.java | 2 +- .../examples/TestFuzzVectorAPI.java | 3 +++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index c31fab41418dc..d4060db4be98a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -131,7 +131,9 @@ final class Operations { new Operation.Binary(Type.ints(), "Short.compareUnsigned(", Type.shorts(), ", ", Type.shorts(), ")", null), new Operation.Unary(Type.ints(), "Short.toUnsignedInt(", Type.shorts(), ")", null), - new Operation.Unary(Type.ints(), "Integer.bitCount(", Type.ints(), ")", null), + // TODO: enable after JDK-8350177 + // new Operation.Unary(Type.ints(), "Integer.bitCount(", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "Integer.compare(", Type.ints(), ", ", Type.ints(), ")", null), new Operation.Binary(Type.ints(), "Integer.compareUnsigned(", Type.ints(), ", ", Type.ints(), ")", null), //new Operation.Binary(Type.ints(), "Integer.compress(", Type.ints(), ", ", Type.ints(), ")", null), @@ -142,10 +144,16 @@ final class Operations { new Operation.Unary(Type.ints(), "Integer.lowestOneBit(", Type.ints(), ")", null), new Operation.Binary(Type.ints(), "Integer.min(", Type.ints(), ", ", Type.ints(), ")", null), new Operation.Binary(Type.ints(), "Integer.max(", Type.ints(), ", ", Type.ints(), ")", null), - new Operation.Unary(Type.ints(), "Integer.numberOfLeadingZeros(", Type.ints(), ")", null), - new Operation.Unary(Type.ints(), "Integer.numberOfTrailingZeros(", Type.ints(), ")", null), + + // TODO: enable after JDK-8350177 + // new Operation.Unary(Type.ints(), "Integer.numberOfLeadingZeros(", Type.ints(), ")", null), + // new Operation.Unary(Type.ints(), "Integer.numberOfTrailingZeros(", Type.ints(), ")", null), + new Operation.Binary(Type.ints(), "Integer.remainderUnsigned(", Type.ints(), ", ", Type.ints(), ")", List.of("ArithmeticException")), - new Operation.Unary(Type.ints(), "Integer.reverse(", Type.ints(), ")", null), + + // TODO: enable after JDK-8350177 + // new Operation.Unary(Type.ints(), "Integer.reverse(", Type.ints(), ")", null), + new Operation.Unary(Type.ints(), "Integer.reverseBytes(", Type.ints(), ")", null), new Operation.Binary(Type.ints(), "Integer.rotateLeft(", Type.ints(), ", ", Type.ints(), ")", null), new Operation.Binary(Type.ints(), "Integer.rotateRight(", Type.ints(), ", ", Type.ints(), ")", null), @@ -387,9 +395,9 @@ private static final List generateVectorAPIOperations() { for (var type : Type.VECTOR_API_VECTOR_TYPES) { ops.add(new Operation.Unary(type, "", type, ".abs()", null)); ops.add(new Operation.Binary(type, "", type, ".add(", type.elementType, ")", null)); - // TODO: add(int e, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".add(", type.elementType, ", ", type.maskType, ")", null)); ops.add(new Operation.Binary(type, "", type, ".add(", type, ")", null)); - // TODO: add(Vector v, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".add(", type, ", ", type.maskType, ")", null)); // If VLENGTH*scale overflows, then a IllegalArgumentException is thrown. ops.add(new Operation.Unary(type, "", type, ".addIndex(1)", null)); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java index 9800a124a7a2a..f2784344d94e2 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java @@ -116,7 +116,7 @@ public boolean isSubtypeOf(Name.Type other) { @Override public final Object con() { // TODO: - return vectorMaskTypeName + ".allTrue()"; + return List.of("VectorMask.fromLong(", vectorType.species, ", ", longs().con(), ")"); } public final int sizeInBits() { return vectorType.sizeInBits(); } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java index d08d5000e01d8..2ede2224b4aa1 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java @@ -167,6 +167,9 @@ public static String generate(CompileFramework comp) { } else if (argType instanceof VectorAPIType vt) { elementType = vt.elementType; args.add(vt.vectorType + ".fromArray(" + vt.species + ", " + name + ", 0)"); + } else if (argType instanceof VectorAPIType.MaskType mt) { + elementType = Type.booleans(); + args.add("VectorMask.fromArray(" + mt.vectorType.species + ", " + name + ", 0)"); } else { throw new RuntimeException("Not handled: " + argType); } From b3a016f18cc7a051acae78f979a78ffe88f09f6b Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 11 Mar 2025 12:12:49 +0100 Subject: [PATCH 167/212] increase compile timeout and rm expand bc of JDK-8351645 --- .../jtreg/compiler/lib/compile_framework/Compile.java | 2 +- .../compiler/lib/template_library/Operations.java | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java b/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java index f7b0d4c375440..e2fc5b8355d12 100644 --- a/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java @@ -39,7 +39,7 @@ * Helper class for compilation of Java and Jasm {@link SourceCode}. */ class Compile { - private static final int COMPILE_TIMEOUT = 60; + private static final int COMPILE_TIMEOUT = 600; // TODO: only temporarily increasing for windows failures. private static final String JAVA_PATH = JDKToolFinder.getJDKTool("java"); private static final String JAVAC_PATH = JDKToolFinder.getJDKTool("javac"); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index d4060db4be98a..6dba660019407 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -139,7 +139,10 @@ final class Operations { //new Operation.Binary(Type.ints(), "Integer.compress(", Type.ints(), ", ", Type.ints(), ")", null), // TODO: add back after JDK-8350896 new Operation.Binary(Type.ints(), "Integer.divideUnsigned(", Type.ints(), ", ", Type.ints(), ")", List.of("ArithmeticException")), - new Operation.Binary(Type.ints(), "Integer.expand(", Type.ints(), ", ", Type.ints(), ")", null), + + // TODO: enamble after JDK-8351645 + // new Operation.Binary(Type.ints(), "Integer.expand(", Type.ints(), ", ", Type.ints(), ")", null), + new Operation.Unary(Type.ints(), "Integer.highestOneBit(", Type.ints(), ")", null), new Operation.Unary(Type.ints(), "Integer.lowestOneBit(", Type.ints(), ")", null), new Operation.Binary(Type.ints(), "Integer.min(", Type.ints(), ", ", Type.ints(), ")", null), @@ -211,8 +214,12 @@ final class Operations { // new Operation.Binary(Type.longs(), "Long.compress(", Type.longs(), ", ", Type.longs(), ")", null), // TODO: add back after JDK-8350896 + // new Operation.Binary(Type.longs(), "Long.divideUnsigned(", Type.longs(), ", ", Type.longs(), ")", List.of("ArithmeticException")), - new Operation.Binary(Type.longs(), "Long.expand(", Type.longs(), ", ", Type.longs(), ")", null), + + // TODO: enable after JDK-8351645 + // new Operation.Binary(Type.longs(), "Long.expand(", Type.longs(), ", ", Type.longs(), ")", null), + new Operation.Unary(Type.longs(), "Long.highestOneBit(", Type.longs(), ")", null), new Operation.Unary(Type.longs(), "Long.lowestOneBit(", Type.longs(), ")", null), new Operation.Binary(Type.longs(), "Long.min(", Type.longs(), ", ", Type.longs(), ")", null), From 2a60dfd96a09cce32777e60f5fe039441aef96a3 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 11 Mar 2025 17:58:03 +0100 Subject: [PATCH 168/212] improve assert --- src/hotspot/share/opto/vectornode.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index dc7fd18a8d07c..d4ebcc55bcfe6 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -1492,7 +1492,8 @@ VectorNode* VectorCastNode::make(int vopc, Node* n1, BasicType bt, uint vlen) { } int VectorCastNode::opcode(int sopc, BasicType bt, bool is_signed) { - assert((is_integral_type(bt) && bt != T_LONG) || is_signed, ""); + assert((is_integral_type(bt) && bt != T_LONG) || is_signed, "sopc=%s, bt=%s, is_signed=%s", + NodeClassNames[sopc], type2name(bt), is_signed ? "true" : "false"); // Handle special case for to/from Half Float conversions switch (sopc) { From fefab60309f9e7b1068604464391bb1c1464507c Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 11 Mar 2025 18:21:01 +0100 Subject: [PATCH 169/212] blend --- .../jtreg/compiler/lib/template_library/Operations.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 6dba660019407..cfb6820e7528d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -422,9 +422,9 @@ private static final List generateVectorAPIOperations() { ops.add(new Operation.Binary(type, "", type, ".or(", type, ")", null)); } - // TODO: blend(int e, VectorMask m) - // TODO: blend(long e, VectorMask m) - // TODO: blend(Vector v, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".blend(", type.elementType, ", ", type.maskType, ")", null)); + ops.add(new Operation.Ternary(type, "", type, ".blend(", Type.longs(), ", ", type.maskType, ")", List.of("IllegalArgumentException"))); + ops.add(new Operation.Ternary(type, "", type, ".blend(", type, ", ", type.maskType, ")", null)); ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", type.elementType, ")", null)); ops.add(new Operation.Unary(type, type.vectorType + ".broadcast(" + type.species + ", ", Type.longs(), ")", List.of("IllegalArgumentException"))); From cf9d07863cbd3a43de45c5848d30bcb14b7db56c Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 12 Mar 2025 10:39:01 +0100 Subject: [PATCH 170/212] fix formatting with backslashes - created issues with windows paths --- .../jtreg/compiler/lib/template_framework/Renderer.java | 2 +- .../template_framework/tests/TestTemplate.java | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 11e38ad72a70b..02f2088b8d5b8 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -282,7 +282,7 @@ private String templateString(String s) { ); return HASHTAG_REPLACEMENT_PATTERN.matcher(temp).replaceAll( // We must escape "$", because it has a special meaning in replaceAll. - (MatchResult result) -> getHashtagReplacement(result.group(1)).replace("$", "\\$") + (MatchResult result) -> getHashtagReplacement(result.group(1)).replace("\\", "\\\\").replace("$", "\\$") ); } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 0bdd76c204e16..0e92fabcdea17 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -179,6 +179,12 @@ public static void testWithOneArguments() { checkEQ(template4.withArgs(0 ).render(), "start 0 end"); checkEQ(template4.withArgs(22 ).render(), "start 22 end"); checkEQ(template4.withArgs(444).render(), "start 444 end"); + + // Test Strings with backslashes: + var template5 = Template.make("a", (String a) -> body("start #a " + a + " end")); + checkEQ(template5.withArgs("/").render(), "start / / end"); + checkEQ(template5.withArgs("\\").render(), "start \\ \\ end"); + checkEQ(template5.withArgs("\\\\").render(), "start \\\\ \\\\ end"); } public static void testWithTwoArguments() { From db8e440c9934834f4e351819705fcdd8f15bde74 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 13 Mar 2025 13:00:53 +0100 Subject: [PATCH 171/212] more ops --- .../lib/template_library/Operations.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index cfb6820e7528d..943c08f9e0c93 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -396,6 +396,19 @@ private record VOP(String name, VOPType type, List elementTypes) new VOP("ZOMO", VOPType.UNARY, Type.INTEGRAL_TYPES) ); + private static final List VECTOR_API_CMP = List.of( + new VOP("EQ", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("GE", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("GT", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("LE", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("LT", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("NE", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("UGE", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("UGT", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("ULE", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("ULT", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES) + ); + private static final List generateVectorAPIOperations() { List ops = new ArrayList(); @@ -436,6 +449,15 @@ private static final List generateVectorAPIOperations() { // Note: check works on class / species, leaving them out. + for (VOP cmp : VECTOR_API_CMP) { + if (cmp.elementTypes().contains(type.elementType)) { + ops.add(new Operation.Binary(type.maskType, "", type, ".compare(VectorOperators." + cmp.name() + ", ", type.elementType, ")", null)); + ops.add(new Operation.Ternary(type.maskType, "", type, ".compare(VectorOperators." + cmp.name() + ", ", type.elementType, ", ", type.maskType, ")", null)); + ops.add(new Operation.Binary(type.maskType, "", type, ".compare(VectorOperators." + cmp.name() + ", ", Type.longs(), ")", List.of("IllegalArgumentException"))); + ops.add(new Operation.Ternary(type.maskType, "", type, ".compare(VectorOperators." + cmp.name() + ", ", Type.longs(), ", ", type.maskType, ")", List.of("IllegalArgumentException"))); + ops.add(new Operation.Binary(type.maskType, "", type, ".compare(VectorOperators." + cmp.name() + ", ", type, ")", null)); + } + } // TODO: compare with VectorMask type // TODO: compress with VectorMask type From 9ff7b75f565770b8baffb3b5da1da1264034e3e7 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 13 Mar 2025 13:39:08 +0100 Subject: [PATCH 172/212] disable FIRST_NONZERO for graal --- .../jtreg/compiler/lib/template_library/Operations.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 943c08f9e0c93..98f7d65382dcd 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -356,7 +356,10 @@ private record VOP(String name, VOPType type, List elementTypes) //new VOP("EXP", VOPType.UNARY, Type.FLOATING_TYPES), new VOP("EXPAND_BITS", VOPType.BINARY, Type.INT_LONG_TYPES), //new VOP("EXPM1", VOPType.UNARY, Type.FLOATING_TYPES), - new VOP("FIRST_NONZERO", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + + // TODO: add back after JDK-8351941 [Graal] + //new VOP("FIRST_NONZERO", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("FMA", VOPType.TERNARY, Type.FLOATING_TYPES), //new VOP("HYPOT", VOPType.BINARY, Type.FLOATING_TYPES), new VOP("LEADING_ZEROS_COUNT", VOPType.UNARY, Type.INTEGRAL_TYPES), From abe5952f7f964531e1b9ca7f5857cbf480f01730 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 13 Mar 2025 14:07:21 +0100 Subject: [PATCH 173/212] more ops --- .../lib/template_library/Operations.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 98f7d65382dcd..45e20616549a4 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -406,10 +406,10 @@ private record VOP(String name, VOPType type, List elementTypes) new VOP("LE", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), new VOP("LT", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), new VOP("NE", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), - new VOP("UGE", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), - new VOP("UGT", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), - new VOP("ULE", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), - new VOP("ULT", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES) + new VOP("UGE", VOPType.ASSOCIATIVE, Type.INTEGRAL_TYPES), + new VOP("UGT", VOPType.ASSOCIATIVE, Type.INTEGRAL_TYPES), + new VOP("ULE", VOPType.ASSOCIATIVE, Type.INTEGRAL_TYPES), + new VOP("ULT", VOPType.ASSOCIATIVE, Type.INTEGRAL_TYPES) ); private static final List generateVectorAPIOperations() { @@ -461,8 +461,8 @@ private static final List generateVectorAPIOperations() { ops.add(new Operation.Binary(type.maskType, "", type, ".compare(VectorOperators." + cmp.name() + ", ", type, ")", null)); } } - // TODO: compare with VectorMask type - // TODO: compress with VectorMask type + + ops.add(new Operation.Binary(type, "", type, ".compress(", type.maskType, ")", null)); // TODO: non zero parts for (var type2 : Type.VECTOR_API_VECTOR_TYPES) { @@ -515,14 +515,14 @@ private static final List generateVectorAPIOperations() { } ops.add(new Operation.Binary(type, "", type, ".div(", type.elementType, ")", List.of("ArithmeticException"))); - // TODO: div(int e, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".div(", type.elementType, ", ", type.maskType, ")", List.of("ArithmeticException"))); ops.add(new Operation.Binary(type, "", type, ".div(", type, ")", List.of("ArithmeticException"))); - // TODO: div(Vector v, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".div(", type.elementType, ", ", type.maskType, ")", List.of("ArithmeticException"))); - // TODO: eq(int e) -> VectorMask - // TODO: eq(Vector v) -> VectorMask + ops.add(new Operation.Binary(type.maskType, "", type, ".eq(", type.elementType, ")", null)); + ops.add(new Operation.Binary(type.maskType, "", type, ".eq(", type, ")", null)); - // TODO: expand(VectorMask m) + ops.add(new Operation.Binary(type, "", type, ".expand(", type.maskType, ")", null)); // TODO: ensure we use all variants of fromArray and fromMemorySegment, plus intoArray and intoMemorySegment. Also: toArray and type variants. From 7e6385cee5db07fa818f72c9dbaae609a2682984 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 13 Mar 2025 14:13:52 +0100 Subject: [PATCH 174/212] more vector ops --- .../compiler/lib/template_library/Operations.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 45e20616549a4..27827890a714f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -534,22 +534,22 @@ private static final List generateVectorAPIOperations() { switch(vop.type()) { case VOPType.UNARY: ops.add(new Operation.Unary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ")", null)); - // TODO: lanewise(VectorOperators.Unary op, VectorMask m) + ops.add(new Operation.Binary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.maskType, ")", null)); break; case VOPType.ASSOCIATIVE: case VOPType.INTEGRAL_ASSOCIATIVE: if (vop.type() == VOPType.ASSOCIATIVE || !type.elementType.isFloating()) { ops.add(new Operation.Unary(type.elementType, "", type, ".reduceLanes(VectorOperators." + vop.name() + ")", null)); - // TODO: reduceLanes(VectorOperators.Associative op, VectorMask m) + ops.add(new Operation.Unary(type.elementType, "", type, ".reduceLanes(VectorOperators." + vop.name() + ", ", type.maskType, ")", null)); } // fall-through case VOPType.BINARY: ops.add(new Operation.Binary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ")", null)); - // TODO: lanewise(VectorOperators.Binary op, int e, VectorMask m) - // TODO: lanewise(VectorOperators.Binary op, long e) - // TODO: lanewise(VectorOperators.Binary op, long e, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type.maskType, ")", null)); + ops.add(new Operation.Binary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", Type.longs(), ")", List.of("IllegalArgumentException"))); + ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", Type.longs(), ", ", type.maskType, ")", List.of("IllegalArgumentException"))); ops.add(new Operation.Binary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ")", null)); - // TODO: lanewise(VectorOperators.Binary op, Vector v, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ", ", type.maskType, ")", null)); break; case VOPType.TERNARY: ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type.elementType, ")", null)); From 821acf6d9c4b9d2c6988f0e872cadd1b7cf63a5e Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 13 Mar 2025 14:26:09 +0100 Subject: [PATCH 175/212] Quaternary and fix --- .../lib/template_library/Expression.java | 18 +++++++++++++++++ .../lib/template_library/Operation.java | 20 ++++++++++++++++++- .../lib/template_library/Operations.java | 2 +- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java index dafa176a4211d..a55e4b2699deb 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Expression.java @@ -167,6 +167,24 @@ private static final ExpressionGeneratorStep expressionGeneratorStep(Type result tokens.add(s3); }; } + case Operation.Quaternary(Type r, String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3, Type t3, String s4, List es) -> { + if (es != null) { exceptions.addAll(es); } + ExpressionGeneratorStep step0 = expressionGeneratorStep(t0, ops, maxDepth-1, argTypes, exceptions); + ExpressionGeneratorStep step1 = expressionGeneratorStep(t1, ops, maxDepth-1, argTypes, exceptions); + ExpressionGeneratorStep step2 = expressionGeneratorStep(t2, ops, maxDepth-1, argTypes, exceptions); + ExpressionGeneratorStep step3 = expressionGeneratorStep(t3, ops, maxDepth-1, argTypes, exceptions); + return (List tokens, List args) -> { + tokens.add(s0); + step0.addTokens(tokens, args); + tokens.add(s1); + step1.addTokens(tokens, args); + tokens.add(s2); + step2.addTokens(tokens, args); + tokens.add(s3); + step3.addTokens(tokens, args); + tokens.add(s4); + }; + } } } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operation.java b/test/hotspot/jtreg/compiler/lib/template_library/Operation.java index c5c86d92fc21f..4b734a303810e 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operation.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operation.java @@ -28,7 +28,8 @@ public sealed interface Operation permits Operation.Unary, Operation.Binary, - Operation.Ternary { + Operation.Ternary, + Operation.Quaternary { /** * Check if input types are in the set. @@ -79,4 +80,21 @@ public boolean matchesTypes(HashSet types) { types.contains(t2); } } + + public static record Quaternary(Type r, String s0, Type t0, String s1, Type t1, String s2, Type t2, String s3, Type t3, String s4, List exceptions) implements Operation { + @Override + public boolean matchesReturnType(Type returnType) { + return r.isSubtypeOf(returnType); + } + + @Override + public boolean matchesTypes(HashSet types) { + return types.contains(r) && + types.contains(t0) && + types.contains(t1) && + types.contains(t2) && + types.contains(t3); + } + } + } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 27827890a714f..b33e4313b25c5 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -540,7 +540,7 @@ private static final List generateVectorAPIOperations() { case VOPType.INTEGRAL_ASSOCIATIVE: if (vop.type() == VOPType.ASSOCIATIVE || !type.elementType.isFloating()) { ops.add(new Operation.Unary(type.elementType, "", type, ".reduceLanes(VectorOperators." + vop.name() + ")", null)); - ops.add(new Operation.Unary(type.elementType, "", type, ".reduceLanes(VectorOperators." + vop.name() + ", ", type.maskType, ")", null)); + ops.add(new Operation.Binary(type.elementType, "", type, ".reduceLanes(VectorOperators." + vop.name() + ", ", type.maskType, ")", null)); } // fall-through case VOPType.BINARY: From fbd63c35e46ee9a56ea1192db0c891e84a5b5b4f Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 13 Mar 2025 15:14:03 +0100 Subject: [PATCH 176/212] masked lanewise ops --- .../jtreg/compiler/lib/template_library/Operation.java | 1 - .../jtreg/compiler/lib/template_library/Operations.java | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operation.java b/test/hotspot/jtreg/compiler/lib/template_library/Operation.java index 4b734a303810e..b10c140ad352a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operation.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operation.java @@ -96,5 +96,4 @@ public boolean matchesTypes(HashSet types) { types.contains(t3); } } - } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index b33e4313b25c5..3d244c4627eaa 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -553,13 +553,13 @@ private static final List generateVectorAPIOperations() { break; case VOPType.TERNARY: ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type.elementType, ")", null)); - // TODO: lanewise(VectorOperators.Ternary op, int e1, int e2, VectorMask m) + ops.add(new Operation.Quaternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type.elementType, ", ", type.maskType, ")", null)); ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type, ")", null)); - // TODO: lanewise(VectorOperators.Ternary op, int e1, Vector v2, VectorMask m) + ops.add(new Operation.Quaternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type, ", ", type.maskType, ")", null)); ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ", ", type.elementType, ")", null)); - // TODO: lanewise(VectorOperators.Ternary op, Vector v1, int e2, VectorMask m) + ops.add(new Operation.Quaternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ", ", type.elementType, ", ", type.maskType, ")", null)); ops.add(new Operation.Ternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ", ", type, ")", null)); - // TODO: lanewise(VectorOperators.Ternary op, Vector v1, Vector v2, VectorMask m) + ops.add(new Operation.Quaternary(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ", ", type, ", ", type.maskType, ")", null)); break; } } From 232d817586e1888aa38dd0286d2c56235ff86125 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 13 Mar 2025 15:28:51 +0100 Subject: [PATCH 177/212] rm ops bc of JDK-8351950 --- .../jtreg/compiler/lib/template_library/Operations.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 3d244c4627eaa..b2b1b6576d2d3 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -368,8 +368,11 @@ private record VOP(String name, VOPType type, List elementTypes) //new VOP("LOG1P", VOPType.UNARY, Type.FLOATING_TYPES), new VOP("LSHL", VOPType.BINARY, Type.INTEGRAL_TYPES), new VOP("LSHR", VOPType.BINARY, Type.INTEGRAL_TYPES), - new VOP("MIN", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), - new VOP("MAX", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + + // TODO: add back after JDK-8351950 + //new VOP("MIN", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + //new VOP("MAX", VOPType.ASSOCIATIVE, Type.PRIMITIVE_TYPES), + new VOP("MUL", VOPType.INTEGRAL_ASSOCIATIVE, Type.PRIMITIVE_TYPES), new VOP("NEG", VOPType.UNARY, Type.PRIMITIVE_TYPES), new VOP("NOT", VOPType.UNARY, Type.INTEGRAL_TYPES), From a354cd5733569117fd257b1f5b6d6fef8a0eda38 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 13 Mar 2025 15:42:59 +0100 Subject: [PATCH 178/212] more masked ops --- .../compiler/lib/template_library/Operations.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index b2b1b6576d2d3..5af866f95c04c 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -576,9 +576,9 @@ private static final List generateVectorAPIOperations() { ops.add(new Operation.Binary(type, "", type, ".min(", type, ")", null)); ops.add(new Operation.Binary(type, "", type, ".mul(", type.elementType, ")", null)); - // TODO: mul(int e, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".mul(", type.elementType, ", ", type.maskType, ")", null)); ops.add(new Operation.Binary(type, "", type, ".mul(", type, ")", null)); - // TODO: mul(Vector v, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".mul(", type, ", ", type.maskType, ")", null)); ops.add(new Operation.Unary(type, "", type, ".neg()", null)); @@ -588,16 +588,16 @@ private static final List generateVectorAPIOperations() { ops.add(new Operation.Binary(type, "", type, ".selectFrom(", type, ")", null)); ops.add(new Operation.Ternary(type, "", type, ".selectFrom(", type, ", ", type, ")", null)); - // TODO: selectFrom(Vector s, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".selectFrom(", type, ", ", type.maskType, ")", null)); ops.add(new Operation.Binary(type, "", type, ".slice(", Type.ints(), ")", List.of("IndexOutOfBoundsException"))); ops.add(new Operation.Ternary(type, "", type, ".slice(", Type.ints(), ", ", type, ")", List.of("IndexOutOfBoundsException"))); - // TODO: slice(int origin, Vector w, VectorMask m) + ops.add(new Operation.Quaternary(type, "", type, ".slice(", Type.ints(), ", ", type, ", ", type.maskType, ")", List.of("IndexOutOfBoundsException"))); ops.add(new Operation.Binary(type, "", type, ".sub(", type.elementType, ")", null)); - // TODO: sub(int e, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".sub(", type.elementType, ", ", type.maskType, ")", null)); ops.add(new Operation.Binary(type, "", type, ".sub(", type, ")", null)); - // TODO: sub(Vector v, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".sub(", type, ", ", type.maskType, ")", null)); // TODO: test(VectorOperators.Test op) // TODO: test(VectorOperators.Test op, VectorMask m) From 2f229b2e20690f9b360ae2f03a1371ae594db56f Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 13 Mar 2025 16:05:13 +0100 Subject: [PATCH 179/212] add test ops --- .../lib/template_library/Operations.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 5af866f95c04c..2356a8b6173fe 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -415,6 +415,14 @@ private record VOP(String name, VOPType type, List elementTypes) new VOP("ULT", VOPType.ASSOCIATIVE, Type.INTEGRAL_TYPES) ); + private static final List VECTOR_API_TEST = List.of( + new VOP("IS_DEFAULT", VOPType.UNARY, Type.PRIMITIVE_TYPES), + new VOP("IS_NEGATIVE", VOPType.UNARY, Type.PRIMITIVE_TYPES), + new VOP("IS_FINITE", VOPType.UNARY, Type.FLOATING_TYPES), + new VOP("IS_NAN", VOPType.UNARY, Type.FLOATING_TYPES), + new VOP("IS_INFINITE", VOPType.UNARY, Type.FLOATING_TYPES) + ); + private static final List generateVectorAPIOperations() { List ops = new ArrayList(); @@ -599,8 +607,13 @@ private static final List generateVectorAPIOperations() { ops.add(new Operation.Binary(type, "", type, ".sub(", type, ")", null)); ops.add(new Operation.Ternary(type, "", type, ".sub(", type, ", ", type.maskType, ")", null)); - // TODO: test(VectorOperators.Test op) - // TODO: test(VectorOperators.Test op, VectorMask m) + + for (VOP test : VECTOR_API_TEST) { + if (test.elementTypes().contains(type.elementType)) { + ops.add(new Operation.Unary(type.maskType, "", type, ".test(VectorOperators." + test.name() + ")", null)); + ops.add(new Operation.Binary(type.maskType, "", type, ".test(VectorOperators." + test.name() + ", ", type.maskType, ")", null)); + } + } ops.add(new Operation.Binary(type, "", type, ".unslice(", Type.ints(), ")", List.of("IndexOutOfBoundsException"))); // TODO: unslice(int origin, Vector w, int part) From c2d5505dc47f6b3c76bc25f294a5721b1a8adbcc Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 13 Mar 2025 16:20:06 +0100 Subject: [PATCH 180/212] more ops --- .../jtreg/compiler/lib/template_library/Operations.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 2356a8b6173fe..01905d5ccd010 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -615,9 +615,10 @@ private static final List generateVectorAPIOperations() { } } + // TODO: non-zero part ops.add(new Operation.Binary(type, "", type, ".unslice(", Type.ints(), ")", List.of("IndexOutOfBoundsException"))); - // TODO: unslice(int origin, Vector w, int part) - // TODO: unslice(int origin, Vector w, int part, VectorMask m) + ops.add(new Operation.Ternary(type, "", type, ".unslice(", Type.ints(), ", ", type ", 0)", List.of("IndexOutOfBoundsException"))); + ops.add(new Operation.Quaternary(type, "", type, ".unslice(", Type.ints(), ", ", type ", 0, ", type.maskType, ")", List.of("IndexOutOfBoundsException"))); ops.add(new Operation.Ternary(type, "", type, ".withLane(", Type.ints(), ", ", type.elementType, ")", List.of("IllegalArgumentException"))); From 2e947416c5fcb58fe55a571565d6614346c0d3d3 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 14 Mar 2025 07:48:32 +0100 Subject: [PATCH 181/212] fix syntax --- .../jtreg/compiler/lib/template_library/Operations.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 01905d5ccd010..524692b5f007a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -617,8 +617,8 @@ private static final List generateVectorAPIOperations() { // TODO: non-zero part ops.add(new Operation.Binary(type, "", type, ".unslice(", Type.ints(), ")", List.of("IndexOutOfBoundsException"))); - ops.add(new Operation.Ternary(type, "", type, ".unslice(", Type.ints(), ", ", type ", 0)", List.of("IndexOutOfBoundsException"))); - ops.add(new Operation.Quaternary(type, "", type, ".unslice(", Type.ints(), ", ", type ", 0, ", type.maskType, ")", List.of("IndexOutOfBoundsException"))); + ops.add(new Operation.Ternary(type, "", type, ".unslice(", Type.ints(), ", ", type, ", 0)", List.of("IndexOutOfBoundsException"))); + ops.add(new Operation.Quaternary(type, "", type, ".unslice(", Type.ints(), ", ", type, ", 0, ", type.maskType, ")", List.of("IndexOutOfBoundsException"))); ops.add(new Operation.Ternary(type, "", type, ".withLane(", Type.ints(), ", ", type.elementType, ")", List.of("IllegalArgumentException"))); From 119ed65532b1ec86f0f386786c73458423a74a64 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 18 Mar 2025 09:06:09 +0100 Subject: [PATCH 182/212] basic vector shuffle --- .../lib/template_library/Library.java | 7 +++-- .../lib/template_library/Operations.java | 6 ++-- .../compiler/lib/template_library/Type.java | 7 ++++- .../lib/template_library/VectorAPIType.java | 29 +++++++++++++++++++ .../examples/TestFuzzVectorAPI.java | 3 ++ 5 files changed, 45 insertions(+), 7 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Library.java b/test/hotspot/jtreg/compiler/lib/template_library/Library.java index b20515642252c..8610d4cad6c4b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Library.java @@ -56,10 +56,11 @@ static T choice(List list) { return list.get(i); } - static final List concat(List a, List b) { + static final List concat(List ... lists) { List list = new ArrayList(); - list.addAll(a); - list.addAll(b); + for (var l : lists) { + list.addAll(l); + } // Ensure the list is immutable. return List.copyOf(list); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index 524692b5f007a..aa914c20eb265 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -590,9 +590,9 @@ private static final List generateVectorAPIOperations() { ops.add(new Operation.Unary(type, "", type, ".neg()", null)); - // TODO: rearrange(VectorShuffle shuffle) - // TODO: rearrange(VectorShuffle s, Vector v) - // TODO: rearrange(VectorShuffle s, VectorMask m) + ops.add(new Operation.Binary(type, "", type, ".rearrange(", type.shuffleType, ")", null)); + ops.add(new Operation.Ternary(type, "", type, ".rearrange(", type.shuffleType, ", ", type, ")", null)); + ops.add(new Operation.Ternary(type, "", type, ".rearrange(", type.shuffleType, ", ", type.maskType, ")", null)); ops.add(new Operation.Binary(type, "", type, ".selectFrom(", type, ")", null)); ops.add(new Operation.Ternary(type, "", type, ".selectFrom(", type, ", ", type, ")", null)); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Type.java b/test/hotspot/jtreg/compiler/lib/template_library/Type.java index 72632c5f3b842..5e80a6da7ab99 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Type.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Type.java @@ -121,6 +121,7 @@ public abstract class Type implements Name.Type { VectorAPIType.DOUBLE_512 ); + // TODO: concat? public static final List VECTOR_API_VECTOR_TYPES = Stream.of( VECTOR_API_BYTE_VECTOR_TYPES, VECTOR_API_SHORT_VECTOR_TYPES, @@ -133,9 +134,13 @@ public abstract class Type implements Name.Type { public static final List VECTOR_API_MASK_TYPES = VECTOR_API_VECTOR_TYPES.stream().map(t -> t.maskType).toList(); + public static final List VECTOR_API_SHUFFLE_TYPES = + VECTOR_API_VECTOR_TYPES.stream().map(t -> t.shuffleType).toList(); + public static final List ALL_VECTOR_API_TYPES = Library.concat( VECTOR_API_VECTOR_TYPES, - VECTOR_API_MASK_TYPES + VECTOR_API_MASK_TYPES, + VECTOR_API_SHUFFLE_TYPES ); public static final List ALL_BUILTIN_TYPES = Library.concat( diff --git a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java index f2784344d94e2..e0e17e0b896ca 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java @@ -68,6 +68,7 @@ public final class VectorAPIType extends Type { public final String species; public final MaskType maskType; + public final ShuffleType shuffleType; private VectorAPIType(PrimitiveType elementType, int length) { this.elementType = elementType; @@ -75,6 +76,7 @@ private VectorAPIType(PrimitiveType elementType, int length) { this.vectorType = elementType.vectorAPITypeName(); this.species = vectorType + ".SPECIES_" + (elementType.sizeInBits() * length); this.maskType = new MaskType(this); + this.shuffleType = new ShuffleType(this); } @Override @@ -121,4 +123,31 @@ public final Object con() { public final int sizeInBits() { return vectorType.sizeInBits(); } } + + public final class ShuffleType extends Type { + public final VectorAPIType vectorType; + public final String vectorShuffleTypeName; + + ShuffleType(VectorAPIType vectorType) { + this.vectorType = vectorType; + this.vectorShuffleTypeName = "VectorShuffle<" + vectorType.elementType.boxedTypeName() + ">"; + } + + @Override + public boolean isSubtypeOf(Name.Type other) { + // TODO: re-evaluate + return this == other; + } + + @Override + public final String name() { return vectorShuffleTypeName; } + + @Override + public final Object con() { + // TODO: + return List.of("VectorShuffle.iota(", vectorType.species, ", ", ints().con(), ", ", ints().con(), ", true)"); + } + + public final int sizeInBits() { return vectorType.sizeInBits(); } + } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java index 2ede2224b4aa1..d78548a219ad8 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzVectorAPI.java @@ -170,6 +170,9 @@ public static String generate(CompileFramework comp) { } else if (argType instanceof VectorAPIType.MaskType mt) { elementType = Type.booleans(); args.add("VectorMask.fromArray(" + mt.vectorType.species + ", " + name + ", 0)"); + } else if (argType instanceof VectorAPIType.ShuffleType st) { + elementType = Type.ints(); + args.add("VectorShuffle.fromArray(" + st.vectorType.species + ", " + name + ", 0)"); } else { throw new RuntimeException("Not handled: " + argType); } From ad2cf40c9927e760ad2c2b16bb023f39b2ac2eaa Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 18 Mar 2025 09:09:03 +0100 Subject: [PATCH 183/212] minor refactor --- test/hotspot/jtreg/compiler/lib/template_library/Type.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Type.java b/test/hotspot/jtreg/compiler/lib/template_library/Type.java index 5e80a6da7ab99..ad51bbb22e2fc 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Type.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Type.java @@ -121,15 +121,14 @@ public abstract class Type implements Name.Type { VectorAPIType.DOUBLE_512 ); - // TODO: concat? - public static final List VECTOR_API_VECTOR_TYPES = Stream.of( + public static final List VECTOR_API_VECTOR_TYPES = Library.concat( VECTOR_API_BYTE_VECTOR_TYPES, VECTOR_API_SHORT_VECTOR_TYPES, VECTOR_API_INT_VECTOR_TYPES, VECTOR_API_LONG_VECTOR_TYPES, VECTOR_API_FLOAT_VECTOR_TYPES, VECTOR_API_DOUBLE_VECTOR_TYPES - ).flatMap((List l) -> l.stream()).toList(); + ); public static final List VECTOR_API_MASK_TYPES = VECTOR_API_VECTOR_TYPES.stream().map(t -> t.maskType).toList(); From af8088534633a3e393104280cbf141bc56e0c68d Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 18 Mar 2025 11:05:26 +0100 Subject: [PATCH 184/212] more todos --- .../jtreg/compiler/lib/template_library/Operations.java | 7 +++++-- .../jtreg/compiler/lib/template_library/VectorAPIType.java | 7 ++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index aa914c20eb265..d7cb1c35e587a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -576,7 +576,8 @@ private static final List generateVectorAPIOperations() { } } - // TODO: lt, maskAll -> VectorMask + ops.add(new Operation.Binary(type.maskType, "", type, ".lt(", type.elementType, ")", null)); + ops.add(new Operation.Binary(type.maskType, "", type, ".lt(", type, ")", null)); ops.add(new Operation.Binary(type, "", type, ".max(", type.elementType, ")", null)); ops.add(new Operation.Binary(type, "", type, ".max(", type, ")", null)); @@ -632,7 +633,9 @@ private static final List generateVectorAPIOperations() { // ops.add(new Operation.Unary(type, "", type, ".sqrt(", type, ")", null)); } - // TODO: toShuffle - VectorShuffle + ops.add(new Operation.Unary(type.shuffleType, "", type, ".toShuffle()", null)); + + // TODO: rest of the ops from ShuffleVector. } // Ensure the list is immutable. diff --git a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java index e0e17e0b896ca..3a6997d689346 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/VectorAPIType.java @@ -81,7 +81,6 @@ private VectorAPIType(PrimitiveType elementType, int length) { @Override public boolean isSubtypeOf(Name.Type other) { - // TODO: re-evaluate return this == other; } @@ -108,7 +107,6 @@ public final class MaskType extends Type { @Override public boolean isSubtypeOf(Name.Type other) { - // TODO: re-evaluate return this == other; } @@ -117,7 +115,7 @@ public boolean isSubtypeOf(Name.Type other) { @Override public final Object con() { - // TODO: + // TODO: more options? return List.of("VectorMask.fromLong(", vectorType.species, ", ", longs().con(), ")"); } @@ -135,7 +133,6 @@ public final class ShuffleType extends Type { @Override public boolean isSubtypeOf(Name.Type other) { - // TODO: re-evaluate return this == other; } @@ -144,7 +141,7 @@ public boolean isSubtypeOf(Name.Type other) { @Override public final Object con() { - // TODO: + // TODO:: more options? return List.of("VectorShuffle.iota(", vectorType.species, ", ", ints().con(), ", ", ints().con(), ", true)"); } From 62f996806658a65aa6cb68fc9f24f52dc162c892 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 18 Mar 2025 11:56:57 +0100 Subject: [PATCH 185/212] wip ClassType --- .../lib/template_library/ClassType.java | 50 ++++++ .../examples/TestFuzzClasses.java | 142 ++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/ClassType.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java diff --git a/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java b/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java new file mode 100644 index 0000000000000..258b52ff60518 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java @@ -0,0 +1,50 @@ +/* + * 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_library; + +import compiler.lib.template_framework.Name; + +public final class ClassType extends Type { + public final String className; + + public ClassType(String className) { + this.className = className; + } + + @Override + public boolean isSubtypeOf(Name.Type other) { + // TODO: subtyping? + return this == other; + } + + @Override + public final String name() { return className; } + + @Override + public final Object con() { + return "new " + className + "()"; + } + + // TODO: template +} diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java new file mode 100644 index 0000000000000..59dfd4464de50 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java @@ -0,0 +1,142 @@ +/* + * 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 + * @summary Test the Template Library's generation of classes. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @compile ../../../compiler/lib/ir_framework/TestFramework.java + * @compile ../../../compiler/lib/generators/Generators.java + * @compile ../../../compiler/lib/verify/Verify.java + * @run driver template_library.examples.TestFuzzClasses + */ + +package template_library.examples; + +import java.util.List; +import java.util.ArrayList; + +import compiler.lib.compile_framework.CompileFramework; + +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateWithArgs; +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_library.Library; +import compiler.lib.template_library.IRTestClass; +import compiler.lib.template_library.Type; +import compiler.lib.template_library.ClassType; + +/** + * TODO + * See {@link TestFuzzExpression} for more explanation about the structure of the test. + */ +public class TestFuzzClasses { + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("p.xyz.InnerTest", generate(comp)); + + // Compile the source file. + comp.compile(); + + // Object ret = p.xyz.InnterTest.main(); + Object ret = comp.invoke("p.xyz.InnerTest", "main", new Object[] {}); + } + + // Generate a source Java file as String + public static String generate(CompileFramework comp) { + // Create the info required for the test class. + // It is imporant that we pass the classpath to the Test-VM, so that it has access + // to all compiled classes. + IRTestClass.Info info = new IRTestClass.Info(comp.getEscapedClassPathOfCompiledClasses(), + "p.xyz", "InnerTest", + List.of("compiler.lib.generators.*", + "compiler.lib.verify.*"), + List.of()); + + var classTemplate = Template.make("classType", (ClassType classType) -> body( + """ + public static class #classType { + public #classType() {} + } + """ + )); + + ArrayList classTypes = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + classTypes.add(new ClassType("C" + i)); + } + + // TODO: description + var template1Body = Template.make("classType", (ClassType classType)-> { + return body( + "return ", classType.con(), ";\n" + ); + }); + var template1 = Template.make("classType", (ClassType classType) -> body( + """ + // --- $test start --- + // classType: #classType + """, + Library.CLASS_HOOK.set( + """ + + static final Object $GOLD = $test(); + + @Test + public static Object $test() { + """, + Library.METHOD_HOOK.set( + template1Body.withArgs(classType) + ), + """ + } + + @Check(test = "$test") + public static void $check(Object result) { + Verify.checkEQ(result, $GOLD); + } + + // --- $test end --- + """ + ) + )); + + // Now use the templates and add them into the IRTestClass. + List templates = new ArrayList<>(); + for (var classType : classTypes) { + templates.add(classTemplate.withArgs(classType)); + } + for (var classType : classTypes) { + templates.add(template1.withArgs(classType)); + } + return IRTestClass.TEMPLATE.withArgs(info, templates).render(); + } +} From d83c7ee4a1f94cb0d314f9eeba0b351d806f207a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 18 Mar 2025 17:45:22 +0100 Subject: [PATCH 186/212] wip refactor Verify --- .../jtreg/compiler/lib/verify/Verify.java | 137 +++++++++++++++++- 1 file changed, 135 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index c18f5525af7cc..edb052f2fb7a9 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -27,8 +27,12 @@ import java.lang.foreign.*; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.ArrayList; + /** + * TODO: update description * The {@link Verify} class provides a single {@link Verify#checkEQ} static method, which recursively * compares the two {@link Object}s by value. It deconstructs {@link Object[]}, compares boxed primitive * types, compares the content of arrays and {@link MemorySegment}s, and checks that the messages of two @@ -39,7 +43,27 @@ */ public final class Verify { - private Verify() {} + // TODO: fields for float exactness, maps, etc. + private final boolean isFloatCheckWithRawBits; + private final boolean isCheckWithArbitraryClasses; + private final HashMap a2b = new HashMap<>(); + private final HashMap a2id = new HashMap<>(); + private final ArrayList id2a = new ArrayList<>(); // TODO: remove? + + private Verify(boolean isFloatCheckWithRawBits, boolean isCheckWithArbitraryClasses) { + this.isFloatCheckWithRawBits = isFloatCheckWithRawBits; + this.isCheckWithArbitraryClasses = isCheckWithArbitraryClasses; + } + + // TODO: desc + public static void checkEQ(Object a, Object b, boolean isFloatCheckWithRawBits, boolean isCheckWithArbitraryClasses) { + Verify v = new Verify(isFloatCheckWithRawBits, isCheckWithArbitraryClasses); + v.checkX(a, b, "root", null, null); + } + + // recursive, so that we have nicer stack trace? - no need for map! + // queue: allows deeper structures + // We need to think about "edges": (a, b) -field-> (c, d) /** * Verify the content of two Objects, possibly recursively. Only limited types are implemented. @@ -49,7 +73,87 @@ private Verify() {} * @throws VerifyException If the comparison fails. */ public static void checkEQ(Object a, Object b) { - checkEQ(a, b, ""); + checkEQ(a, b, false, false); + } + + private void checkX(Object a, Object b, String field, Object aParent, Object bParent) { + // Both null + if (a == null && b == null) { + return; + } + + // Null mismatch + if (a == null || b == null) { + System.err.println("ERROR: Verify.checkEQ failed: null mismatch"); + printX(a, b, field, aParent, bParent); + throw new VerifyException("Object array null mismatch."); + } + + // Class mismatch + Class ca = a.getClass(); + Class cb = b.getClass(); + if (ca != cb) { + System.err.println("ERROR: Verify.checkEQ failed: class mismatch."); + System.err.println(" " + ca.getName() + " vs " + cb.getName()); + printX(a, b, field, aParent, bParent); + throw new VerifyException("Object class mismatch."); + } + + // Already visited? + if (checkAlreadyVisited(a, b, field, aParent, bParent)) { + return; + } + // FIXME: continue here! + + String context = "TODO rm"; + + switch (a) { + case Object[] x -> checkEQimpl(x, (Object[])b, context); + case Byte x -> checkEQimpl(x, ((Byte)b).byteValue(), context); + case Character x -> checkEQimpl(x, ((Character)b).charValue(), context); + case Short x -> checkEQimpl(x, ((Short)b).shortValue(), context); + case Integer x -> checkEQimpl(x, ((Integer)b).intValue(), context); + case Long x -> checkEQimpl(x, ((Long)b).longValue(), context); + case Float x -> checkEQimpl(x, ((Float)b).floatValue(), context); + case Double x -> checkEQimpl(x, ((Double)b).doubleValue(), context); + case Boolean x -> checkEQimpl(x, ((Boolean)b).booleanValue(),context); + case byte[] x -> checkEQimpl(x, (byte[])b, context); + case char[] x -> checkEQimpl(x, (char[])b, context); + case short[] x -> checkEQimpl(x, (short[])b, context); + case int[] x -> checkEQimpl(x, (int[])b, context); + case long[] x -> checkEQimpl(x, (long[])b, context); + case float[] x -> checkEQimpl(x, (float[])b, context); + case double[] x -> checkEQimpl(x, (double[])b, context); + case boolean[] x -> checkEQimpl(x, (boolean[])b, context); + case MemorySegment x -> checkEQimpl(x, (MemorySegment) b, context); + case Exception x -> checkEQimpl(x, (Exception) b, context); + default -> { + if (ca.getName().startsWith("jdk.incubator.vector") && ca.getName().contains("Vector")) { + // We do not want to import jdk.incubator.vector explicitly, because it would mean we would also have + // to add "--add-modules=jdk.incubator.vector" to the command-line of every test that uses the Verify + // class. So we hack this via reflection. + Object va = null; + Object vb = null; + try { + Method m = ca.getMethod("toArray"); + va = m.invoke(a); + vb = m.invoke(b); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Could not invoke toArray on " + ca.getName(), e); + } catch (IllegalAccessException e) { + throw new RuntimeException("Could not invoke toArray on " + ca.getName(), e); + } catch (InvocationTargetException e) { + throw new RuntimeException("Could not invoke toArray on " + ca.getName(), e); + } + checkEQ(va, vb, context); + return; + } + + System.err.println("ERROR: Verify.checkEQ failed: type not supported: " + ca.getName()); + printX(a, b, field, aParent, bParent); + throw new VerifyException("Object type not supported: " + ca.getName()); + } + } } /** @@ -381,6 +485,35 @@ private static void print(Object a, String context) { } } + private void printX(Object a, Object b, String field, Object aParent, Object bParent) { + System.err.println(" aParent: " + aParent); + System.err.println(" bParent: " + bParent); + System.err.println(" field: " + field); + System.err.println(" a: " + a); + System.err.println(" b: " + b); + } + + private boolean checkAlreadyVisited(Object a, Object b, String field, Object aParent, Object bParent) { + Integer id = a2id.get(a); + if (id == null) { + // Record for next time. + id = id2a.size(); + a2id.put(a, id); + a2b.put(a, b); + id2a.add(a); + return false; + } else { + Object bPrevious = a2b.get(a); + if (b != bPrevious) { + System.err.println("ERROR: Verify.checkEQ failed:"); + printX(a, b, field, aParent, bParent); + System.err.println(" bPrevious: " + bPrevious); + throw new VerifyException("Mismatch with previous pair."); + } + return true; + } + } + private static void printMemorySegment(MemorySegment a, String context) { Optional maybeBase = a.heapBase(); System.err.println(" " + context + " via MemorySegment:"); From 8e996b6af7fdc8886afcc28cb0c27c842a2fc5cd Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 19 Mar 2025 07:35:29 +0100 Subject: [PATCH 187/212] context / field refactor --- .../jtreg/compiler/lib/verify/Verify.java | 245 ++++++------------ 1 file changed, 86 insertions(+), 159 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index edb052f2fb7a9..eb66b403626d9 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -58,7 +58,7 @@ private Verify(boolean isFloatCheckWithRawBits, boolean isCheckWithArbitraryClas // TODO: desc public static void checkEQ(Object a, Object b, boolean isFloatCheckWithRawBits, boolean isCheckWithArbitraryClasses) { Verify v = new Verify(isFloatCheckWithRawBits, isCheckWithArbitraryClasses); - v.checkX(a, b, "root", null, null); + v.checkEQdispatch(a, b, "", null, null); } // recursive, so that we have nicer stack trace? - no need for map! @@ -76,7 +76,7 @@ public static void checkEQ(Object a, Object b) { checkEQ(a, b, false, false); } - private void checkX(Object a, Object b, String field, Object aParent, Object bParent) { + private void checkEQdispatch(Object a, Object b, String field, Object aParent, Object bParent) { // Both null if (a == null && b == null) { return; @@ -103,30 +103,27 @@ private void checkX(Object a, Object b, String field, Object aParent, Object bPa if (checkAlreadyVisited(a, b, field, aParent, bParent)) { return; } - // FIXME: continue here! - - String context = "TODO rm"; switch (a) { - case Object[] x -> checkEQimpl(x, (Object[])b, context); - case Byte x -> checkEQimpl(x, ((Byte)b).byteValue(), context); - case Character x -> checkEQimpl(x, ((Character)b).charValue(), context); - case Short x -> checkEQimpl(x, ((Short)b).shortValue(), context); - case Integer x -> checkEQimpl(x, ((Integer)b).intValue(), context); - case Long x -> checkEQimpl(x, ((Long)b).longValue(), context); - case Float x -> checkEQimpl(x, ((Float)b).floatValue(), context); - case Double x -> checkEQimpl(x, ((Double)b).doubleValue(), context); - case Boolean x -> checkEQimpl(x, ((Boolean)b).booleanValue(),context); - case byte[] x -> checkEQimpl(x, (byte[])b, context); - case char[] x -> checkEQimpl(x, (char[])b, context); - case short[] x -> checkEQimpl(x, (short[])b, context); - case int[] x -> checkEQimpl(x, (int[])b, context); - case long[] x -> checkEQimpl(x, (long[])b, context); - case float[] x -> checkEQimpl(x, (float[])b, context); - case double[] x -> checkEQimpl(x, (double[])b, context); - case boolean[] x -> checkEQimpl(x, (boolean[])b, context); - case MemorySegment x -> checkEQimpl(x, (MemorySegment) b, context); - case Exception x -> checkEQimpl(x, (Exception) b, context); + case Object[] x -> checkEQimpl(x, (Object[])b, field, aParent, bParent); + case Byte x -> checkEQimpl(x, ((Byte)b).byteValue(), field, aParent, bParent); + case Character x -> checkEQimpl(x, ((Character)b).charValue(), field, aParent, bParent); + case Short x -> checkEQimpl(x, ((Short)b).shortValue(), field, aParent, bParent); + case Integer x -> checkEQimpl(x, ((Integer)b).intValue(), field, aParent, bParent); + case Long x -> checkEQimpl(x, ((Long)b).longValue(), field, aParent, bParent); + case Float x -> checkEQimpl(x, ((Float)b).floatValue(), field, aParent, bParent); + case Double x -> checkEQimpl(x, ((Double)b).doubleValue(), field, aParent, bParent); + case Boolean x -> checkEQimpl(x, ((Boolean)b).booleanValue(), field, aParent, bParent); + case byte[] x -> checkEQimpl(x, (byte[])b, field, aParent, bParent); + case char[] x -> checkEQimpl(x, (char[])b, field, aParent, bParent); + case short[] x -> checkEQimpl(x, (short[])b, field, aParent, bParent); + case int[] x -> checkEQimpl(x, (int[])b, field, aParent, bParent); + case long[] x -> checkEQimpl(x, (long[])b, field, aParent, bParent); + case float[] x -> checkEQimpl(x, (float[])b, field, aParent, bParent); + case double[] x -> checkEQimpl(x, (double[])b, field, aParent, bParent); + case boolean[] x -> checkEQimpl(x, (boolean[])b, field, aParent, bParent); + case MemorySegment x -> checkEQimpl(x, (MemorySegment) b, field, aParent, bParent); + case Exception x -> checkEQimpl(x, (Exception) b, field, aParent, bParent); default -> { if (ca.getName().startsWith("jdk.incubator.vector") && ca.getName().contains("Vector")) { // We do not want to import jdk.incubator.vector explicitly, because it would mean we would also have @@ -145,7 +142,7 @@ private void checkX(Object a, Object b, String field, Object aParent, Object bPa } catch (InvocationTargetException e) { throw new RuntimeException("Could not invoke toArray on " + ca.getName(), e); } - checkEQ(va, vb, context); + checkEQdispatch(va, vb, field + ".toArray", aParent, bParent); return; } @@ -156,90 +153,13 @@ private void checkX(Object a, Object b, String field, Object aParent, Object bPa } } - /** - * Verify the content of two Objects, possibly recursively. Only limited types are implemented. - */ - private static void checkEQ(Object a, Object b, String context) { - // Both null - if (a == null && b == null) { - return; - } - - // Null mismatch - if (a == null || b == null) { - System.err.println("ERROR: Verify.checkEQ failed: null mismatch"); - print(a, "a " + context); - print(b, "b " + context); - throw new VerifyException("Object array null mismatch."); - } - - // Class mismatch - Class ca = a.getClass(); - Class cb = b.getClass(); - if (ca != cb) { - System.err.println("ERROR: Verify.checkEQ failed: class mismatch."); - System.err.println(" " + ca.getName() + " vs " + cb.getName()); - print(a, "a " + context); - print(b, "b " + context); - throw new VerifyException("Object class mismatch."); - } - - switch (a) { - case Object[] x -> checkEQimpl(x, (Object[])b, context); - case Byte x -> checkEQimpl(x, ((Byte)b).byteValue(), context); - case Character x -> checkEQimpl(x, ((Character)b).charValue(), context); - case Short x -> checkEQimpl(x, ((Short)b).shortValue(), context); - case Integer x -> checkEQimpl(x, ((Integer)b).intValue(), context); - case Long x -> checkEQimpl(x, ((Long)b).longValue(), context); - case Float x -> checkEQimpl(x, ((Float)b).floatValue(), context); - case Double x -> checkEQimpl(x, ((Double)b).doubleValue(), context); - case Boolean x -> checkEQimpl(x, ((Boolean)b).booleanValue(),context); - case byte[] x -> checkEQimpl(x, (byte[])b, context); - case char[] x -> checkEQimpl(x, (char[])b, context); - case short[] x -> checkEQimpl(x, (short[])b, context); - case int[] x -> checkEQimpl(x, (int[])b, context); - case long[] x -> checkEQimpl(x, (long[])b, context); - case float[] x -> checkEQimpl(x, (float[])b, context); - case double[] x -> checkEQimpl(x, (double[])b, context); - case boolean[] x -> checkEQimpl(x, (boolean[])b, context); - case MemorySegment x -> checkEQimpl(x, (MemorySegment) b, context); - case Exception x -> checkEQimpl(x, (Exception) b, context); - default -> { - if (ca.getName().startsWith("jdk.incubator.vector") && ca.getName().contains("Vector")) { - // We do not want to import jdk.incubator.vector explicitly, because it would mean we would also have - // to add "--add-modules=jdk.incubator.vector" to the command-line of every test that uses the Verify - // class. So we hack this via reflection. - Object va = null; - Object vb = null; - try { - Method m = ca.getMethod("toArray"); - va = m.invoke(a); - vb = m.invoke(b); - } catch (NoSuchMethodException e) { - throw new RuntimeException("Could not invoke toArray on " + ca.getName(), e); - } catch (IllegalAccessException e) { - throw new RuntimeException("Could not invoke toArray on " + ca.getName(), e); - } catch (InvocationTargetException e) { - throw new RuntimeException("Could not invoke toArray on " + ca.getName(), e); - } - checkEQ(va, vb, context); - return; - } - - System.err.println("ERROR: Verify.checkEQ failed: type not supported: " + ca.getName()); - print(a, "a " + context); - print(b, "b " + context); - throw new VerifyException("Object type not supported: " + ca.getName()); - } - } - } - /** * Verify that two bytes are identical. */ - private static void checkEQimpl(byte a, byte b, String context) { + private void checkEQimpl(byte a, byte b, String field, Object aParent, Object bParent) { if (a != b) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + a + " vs " + b + " for " + context); + System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + a + " vs " + b); + printX(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } @@ -247,9 +167,10 @@ private static void checkEQimpl(byte a, byte b, String context) { /** * Verify that two chars are identical. */ - private static void checkEQimpl(char a, char b, String context) { + private void checkEQimpl(char a, char b, String field, Object aParent, Object bParent) { if (a != b) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + (int)a + " vs " + (int)b + " for " + context); + System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + (int)a + " vs " + (int)b); + printX(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + (int)a + " vs " + (int)b); } } @@ -257,9 +178,10 @@ private static void checkEQimpl(char a, char b, String context) { /** * Verify that two shorts are identical. */ - private static void checkEQimpl(short a, short b, String context) { + private void checkEQimpl(short a, short b, String field, Object aParent, Object bParent) { if (a != b) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + (int)a + " vs " + (int)b + " for " + context); + System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + (int)a + " vs " + (int)b); + printX(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + (int)a + " vs " + (int)b); } } @@ -267,9 +189,10 @@ private static void checkEQimpl(short a, short b, String context) { /** * Verify that two ints are identical. */ - private static void checkEQimpl(int a, int b, String context) { + private void checkEQimpl(int a, int b, String field, Object aParent, Object bParent) { if (a != b) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + a + " vs " + b + " for " + context); + System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + a + " vs " + b); + printX(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } @@ -277,9 +200,10 @@ private static void checkEQimpl(int a, int b, String context) { /** * Verify that two longs are identical. */ - private static void checkEQimpl(long a, long b, String context) { + private void checkEQimpl(long a, long b, String field, Object aParent, Object bParent) { if (a != b) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + a + " vs " + b + " for " + context); + System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + a + " vs " + b); + printX(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } @@ -291,11 +215,12 @@ private static void checkEQimpl(long a, long b, String context) { * of two NaN values should always return the first of the two). So we verify that we have the same bit * pattern in all cases, except for NaN we project to the canonical NaN, using Float.floatToIntBits. */ - private static void checkEQimpl(float a, float b, String context) { + private void checkEQimpl(float a, float b, String field, Object aParent, Object bParent) { if (Float.floatToIntBits(a) != Float.floatToIntBits(b)) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch for " + context); + System.err.println("ERROR: Verify.checkEQ failed: value mismatch"); System.err.println(" Values: " + a + " vs " + b); System.err.println(" Values: " + Float.floatToRawIntBits(a) + " vs " + Float.floatToRawIntBits(b)); + printX(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } @@ -303,11 +228,12 @@ private static void checkEQimpl(float a, float b, String context) { /** * Same as Float case, see above. */ - private static void checkEQimpl(double a, double b, String context) { + private void checkEQimpl(double a, double b, String field, Object aParent, Object bParent) { if (Double.doubleToLongBits(a) != Double.doubleToLongBits(b)) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch for " + context); + System.err.println("ERROR: Verify.checkEQ failed: value mismatch"); System.err.println(" Values: " + a + " vs " + b); System.err.println(" Values: " + Double.doubleToRawLongBits(a) + " vs " + Double.doubleToRawLongBits(b)); + printX(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } @@ -315,9 +241,10 @@ private static void checkEQimpl(double a, double b, String context) { /** * Verify that two booleans are identical. */ - private static void checkEQimpl(boolean a, boolean b, String context) { + private void checkEQimpl(boolean a, boolean b, String field, Object aParent, Object bParent) { if (a != b) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + a + " vs " + b + " for " + context); + System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + a + " vs " + b); + printX(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } @@ -326,15 +253,16 @@ private static void checkEQimpl(boolean a, boolean b, String context) { * Verify that the content of two MemorySegments is identical. Note: we do not check the * backing type, only the size and content. */ - private static void checkEQimpl(MemorySegment a, MemorySegment b, String context) { + private void checkEQimpl(MemorySegment a, MemorySegment b, String field, Object aParent, Object bParent) { long offset = a.mismatch(b); if (offset == -1) { return; } // Print some general info - System.err.println("ERROR: Verify.checkEQ failed for: " + context); + System.err.println("ERROR: Verify.checkEQ failed"); - printMemorySegment(a, "a " + context); - printMemorySegment(b, "b " + context); + printX(a, b, field, aParent, bParent); + printMemorySegment(a, "a"); + printMemorySegment(b, "b"); // (1) Mismatch on size if (a.byteSize() != b.byteSize()) { @@ -352,7 +280,7 @@ private static void checkEQimpl(MemorySegment a, MemorySegment b, String context * Verify that the content of two MemorySegments is identical. Note: we do not check the * backing type, only the size and content. */ - private static void checkEQimpl(Exception a, Exception b, String context) { + private void checkEQimpl(Exception a, Exception b, String field, Object aParent, Object bParent) { String am = a.getMessage(); String bm = b.getMessage(); @@ -360,47 +288,46 @@ private static void checkEQimpl(Exception a, Exception b, String context) { if (am == null || bm == null) { return; } if (am.equals(bm)) { return; } - System.err.println("ERROR: Verify.checkEQ failed for: " + context); + System.err.println("ERROR: Verify.checkEQ failed:"); System.out.println("a: " + a.getMessage()); System.out.println("b: " + b.getMessage()); - System.out.println(a); - System.out.println(b); + printX(a, b, field, aParent, bParent); throw new VerifyException("Exception message mismatch: " + a + " vs " + b); } /** * Verify that the content of two byte arrays is identical. */ - private static void checkEQimpl(byte[] a, byte[] b, String context) { - checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), context); + private void checkEQimpl(byte[] a, byte[] b, String field, Object aParent, Object bParent) { + checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), field + " -> to MemorySegment", aParent, bParent); } /** * Verify that the content of two char arrays is identical. */ - private static void checkEQimpl(char[] a, char[] b, String context) { - checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), context); + private void checkEQimpl(char[] a, char[] b, String field, Object aParent, Object bParent) { + checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), field + " -> to MemorySegment", aParent, bParent); } /** * Verify that the content of two short arrays is identical. */ - private static void checkEQimpl(short[] a, short[] b, String context) { - checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), context); + private void checkEQimpl(short[] a, short[] b, String field, Object aParent, Object bParent) { + checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), field + " -> to MemorySegment", aParent, bParent); } /** * Verify that the content of two int arrays is identical. */ - private static void checkEQimpl(int[] a, int[] b, String context) { - checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), context); + private void checkEQimpl(int[] a, int[] b, String field, Object aParent, Object bParent) { + checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), field + " -> to MemorySegment", aParent, bParent); } /** * Verify that the content of two long arrays is identical. */ - private static void checkEQimpl(long[] a, long[] b, String context) { - checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), context); + private void checkEQimpl(long[] a, long[] b, String field, Object aParent, Object bParent) { + checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), field + " -> to MemorySegment", aParent, bParent); } /** @@ -411,15 +338,17 @@ private static void checkEQimpl(long[] a, long[] b, String context) { * of two NaN values should always return the first of the two). So we verify that we have the same bit * pattern in all cases, except for NaN we project to the canonical NaN, using Float.floatToIntBits. */ - private static void checkEQimpl(float[] a, float[] b, String context) { + private void checkEQimpl(float[] a, float[] b, String field, Object aParent, Object bParent) { if (a.length != b.length) { - System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length + " vs " + b.length + " " + context); + System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length + " vs " + b.length); + printX(a, b, field, aParent, bParent); throw new VerifyException("Float array length mismatch."); } for (int i = 0; i < a.length; i++) { if (Float.floatToIntBits(a[i]) != Float.floatToIntBits(b[i])) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i] + " " + context); + System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i]); + printX(a, b, field, aParent, bParent); throw new VerifyException("Float array value mismatch " + a[i] + " vs " + b[i]); } } @@ -429,15 +358,17 @@ private static void checkEQimpl(float[] a, float[] b, String context) { * Verify that the content of two double arrays is identical. * Same issue with NaN as above for floats. */ - private static void checkEQimpl(double[] a, double[] b, String context) { + private void checkEQimpl(double[] a, double[] b, String field, Object aParent, Object bParent) { if (a.length != b.length) { - System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length + " vs " + b.length + " " + context); + System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length + " vs " + b.length); + printX(a, b, field, aParent, bParent); throw new VerifyException("Double array length mismatch."); } for (int i = 0; i < a.length; i++) { if (Double.doubleToLongBits(a[i]) != Double.doubleToLongBits(b[i])) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i] + " " + context); + System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i]); + printX(a, b, field, aParent, bParent); throw new VerifyException("Double array value mismatch " + a[i] + " vs " + b[i]); } } @@ -446,15 +377,17 @@ private static void checkEQimpl(double[] a, double[] b, String context) { /** * Verify that the content of two boolean arrays is identical. */ - private static void checkEQimpl(boolean[] a, boolean[] b, String context) { + private void checkEQimpl(boolean[] a, boolean[] b, String field, Object aParent, Object bParent) { if (a.length != b.length) { - System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length + " vs " + b.length + " " + context); + System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length + " vs " + b.length); + printX(a, b, field, aParent, bParent); throw new VerifyException("Boolean array length mismatch."); } for (int i = 0; i < a.length; i++) { if (a[i] != b[i]) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i] + " " + context); + System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i]); + printX(a, b, field, aParent, bParent); throw new VerifyException("Boolean array value mismatch."); } } @@ -464,24 +397,17 @@ private static void checkEQimpl(boolean[] a, boolean[] b, String context) { * Verify that the content of two Object arrays is identical, recursively: * every element is compared with checkEQimpl for the corresponding type. */ - private static void checkEQimpl(Object[] a, Object[] b, String context) { + private void checkEQimpl(Object[] a, Object[] b, String field, Object aParent, Object bParent) { // (1) Length mismatch if (a.length != b.length) { System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length + " vs " + b.length); + printX(a, b, field, aParent, bParent); throw new VerifyException("Object array length mismatch."); } for (int i = 0; i < a.length; i++) { // Recursive checkEQ call. - checkEQ(a[i], b[i], "[" + i + "]" + context); - } - } - - private static void print(Object a, String context) { - if (a == null) { - System.err.println(" " + context + ": null"); - } else { - System.err.println(" " + context + ": " + a); + checkEQdispatch(a[i], b[i], "[" + i + "]", a, b); } } @@ -494,6 +420,7 @@ private void printX(Object a, Object b, String field, Object aParent, Object bPa } private boolean checkAlreadyVisited(Object a, Object b, String field, Object aParent, Object bParent) { + // TODO: must also check reverse direction? Integer id = a2id.get(a); if (id == null) { // Record for next time. @@ -514,9 +441,9 @@ private boolean checkAlreadyVisited(Object a, Object b, String field, Object aPa } } - private static void printMemorySegment(MemorySegment a, String context) { + private void printMemorySegment(MemorySegment a, String name) { Optional maybeBase = a.heapBase(); - System.err.println(" " + context + " via MemorySegment:"); + System.err.println(" MemorySegment " + name + ":"); if (maybeBase.isEmpty()) { System.err.println(" no heap base (native)."); } else { @@ -527,7 +454,7 @@ private static void printMemorySegment(MemorySegment a, String context) { System.err.println(" byteSize: " + a.byteSize()); } - private static void printMemorySegmentValue(MemorySegment a, long offset, int range) { + private void printMemorySegmentValue(MemorySegment a, long offset, int range) { long start = Long.max(offset - range, 0); long end = Long.min(offset + range, a.byteSize()); for (long i = start; i < end; i++) { From 7c2ff77058a957bb96986affd621c06d15d33746 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 19 Mar 2025 08:12:31 +0100 Subject: [PATCH 188/212] impl and test isFloatCheckWithRawBits --- .../jtreg/compiler/lib/verify/Verify.java | 57 ++++++++++++------- .../verify/tests/TestVerify.java | 44 +++++++++++++- 2 files changed, 78 insertions(+), 23 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index eb66b403626d9..5fa81c50fa940 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -39,6 +39,8 @@ * {@link Exception}s are equal. We also check for equivalent content in {@link Vector}s from the Vector * API. * + * TODO: mention that MemorySegment is always checked raw, also for floats. + * * When a comparison fail, then methods print helpful messages, before throwing a {@link VerifyException}. */ public final class Verify { @@ -209,30 +211,49 @@ private void checkEQimpl(long a, long b, String field, Object aParent, Object bP } /** + * There are two comparison modes: one where we compare the raw bits, which sees different NaN + * encodings as different values, and one where we see all NaN encodings as identical. * Ideally, we would want to assert that the Float.floatToRawIntBits are identical. * But the Java spec allows us to return different bits for a NaN, which allows swapping the inputs * of an add or mul (NaN1 * NaN2 does not have same bits as NaN2 * NaN1, because the multiplication - * of two NaN values should always return the first of the two). So we verify that we have the same bit + * of two NaN values should always return the first of the two). + * Hence, by default, we pick the non-raw coparison: we verify that we have the same bit * pattern in all cases, except for NaN we project to the canonical NaN, using Float.floatToIntBits. */ + private boolean isFloatEQ(float a, float b) { + return isFloatCheckWithRawBits ? Float.floatToRawIntBits(a) != Float.floatToRawIntBits(b) + : Float.floatToIntBits(a) != Float.floatToIntBits(b); + } + + /** + * See comments for "isFloatEQ". + */ + private boolean isDoubleEQ(double a, double b) { + return isFloatCheckWithRawBits ? Double.doubleToRawLongBits(a) != Double.doubleToRawLongBits(b) + : Double.doubleToLongBits(a) != Double.doubleToLongBits(b); + } + + /** + * Check that two floats are equal according to "isFloatEQ". + */ private void checkEQimpl(float a, float b, String field, Object aParent, Object bParent) { - if (Float.floatToIntBits(a) != Float.floatToIntBits(b)) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch"); - System.err.println(" Values: " + a + " vs " + b); - System.err.println(" Values: " + Float.floatToRawIntBits(a) + " vs " + Float.floatToRawIntBits(b)); + if (isFloatEQ(a, b)) { + System.err.println("ERROR: Verify.checkEQ failed: value mismatch. check raw: " + isFloatCheckWithRawBits); + System.err.println(" Values: " + a + " vs " + b); + System.err.println(" Raw: " + Float.floatToRawIntBits(a) + " vs " + Float.floatToRawIntBits(b)); printX(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } /** - * Same as Float case, see above. + * Check that two doubles are equal according to "isDoubleEQ". */ private void checkEQimpl(double a, double b, String field, Object aParent, Object bParent) { - if (Double.doubleToLongBits(a) != Double.doubleToLongBits(b)) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch"); + if (isDoubleEQ(a, b)) { + System.err.println("ERROR: Verify.checkEQ failed: value mismatch. check raw: " + isFloatCheckWithRawBits); System.err.println(" Values: " + a + " vs " + b); - System.err.println(" Values: " + Double.doubleToRawLongBits(a) + " vs " + Double.doubleToRawLongBits(b)); + System.err.println(" Raw: " + Double.doubleToRawLongBits(a) + " vs " + Double.doubleToRawLongBits(b)); printX(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } @@ -331,12 +352,7 @@ private void checkEQimpl(long[] a, long[] b, String field, Object aParent, Objec } /** - * Verify that the content of two float arrays is identical. - * Ideally, we would want to assert that the Float.floatToRawIntBits are identical. - * But the Java spec allows us to return different bits for a NaN, which allows swapping the inputs - * of an add or mul (NaN1 * NaN2 does not have same bits as NaN2 * NaN1, because the multiplication - * of two NaN values should always return the first of the two). So we verify that we have the same bit - * pattern in all cases, except for NaN we project to the canonical NaN, using Float.floatToIntBits. + * Check that two float arrays are equal according to "isFloatEQ". */ private void checkEQimpl(float[] a, float[] b, String field, Object aParent, Object bParent) { if (a.length != b.length) { @@ -346,8 +362,8 @@ private void checkEQimpl(float[] a, float[] b, String field, Object aParent, Obj } for (int i = 0; i < a.length; i++) { - if (Float.floatToIntBits(a[i]) != Float.floatToIntBits(b[i])) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i]); + if (isFloatEQ(a[i], b[i])) { + System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i] + ". check raw: " + isFloatCheckWithRawBits); printX(a, b, field, aParent, bParent); throw new VerifyException("Float array value mismatch " + a[i] + " vs " + b[i]); } @@ -355,8 +371,7 @@ private void checkEQimpl(float[] a, float[] b, String field, Object aParent, Obj } /** - * Verify that the content of two double arrays is identical. - * Same issue with NaN as above for floats. + * Check that two double arrays are equal according to "isDoubleEQ". */ private void checkEQimpl(double[] a, double[] b, String field, Object aParent, Object bParent) { if (a.length != b.length) { @@ -366,8 +381,8 @@ private void checkEQimpl(double[] a, double[] b, String field, Object aParent, O } for (int i = 0; i < a.length; i++) { - if (Double.doubleToLongBits(a[i]) != Double.doubleToLongBits(b[i])) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i]); + if (isDoubleEQ(a[i], b[i])) { + System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i] + ". check raw: " + isFloatCheckWithRawBits); printX(a, b, field, aParent, bParent); throw new VerifyException("Double array value mismatch " + a[i] + " vs " + b[i]); } diff --git a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java index 2b5111edc15cc..08b08597f415e 100644 --- a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java +++ b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java @@ -52,6 +52,8 @@ public static void main(String[] args) { testNativeMemorySegment(); testException(); + testRawFloat(); + // Test recursive data: Object array of values, etc. testRecursive(); } @@ -314,6 +316,40 @@ public static void testException() { checkNE(e4, e5); } + public static void testRawFloat() { + float nanF1 = Float.intBitsToFloat(0x7f800001); + float nanF2 = Float.intBitsToFloat(0x7fffffff); + double nanD1 = Double.longBitsToDouble(0x7ff0000000000001L); + double nanD2 = Double.longBitsToDouble(0x7fffffffffffffffL); + + float[] arrF1 = new float[]{nanF1}; + float[] arrF2 = new float[]{nanF2}; + double[] arrD1 = new double[]{nanD1}; + double[] arrD2 = new double[]{nanD2}; + + Verify.checkEQ(nanF1, Float.NaN); + Verify.checkEQ(nanF1, nanF1); + Verify.checkEQ(nanF1, nanF1, true, false); + Verify.checkEQ(nanF1, nanF2); + Verify.checkEQ(nanD1, Double.NaN); + Verify.checkEQ(nanD1, nanD1); + Verify.checkEQ(nanD1, nanD1, true, false); + Verify.checkEQ(nanD1, nanD2); + + Verify.checkEQ(arrF1, arrF1); + Verify.checkEQ(arrF1, arrF1, true, false); + Verify.checkEQ(arrF1, arrF2); + Verify.checkEQ(arrD1, arrD1); + Verify.checkEQ(arrD1, arrD1, true, false); + Verify.checkEQ(arrD1, arrD2); + + checkNE(nanF1, nanF2, true, false); + checkNE(nanD1, nanD2, true, false); + + checkNE(arrF1, arrF2, true, false); + checkNE(arrD1, arrD2, true, false); + } + public static void testRecursive() { Verify.checkEQ(null, null); @@ -434,10 +470,14 @@ public static void testRecursive() { } } - public static void checkNE(Object a, Object b) { + public static void checkNE(Object a, Object b, boolean isFloatCheckWithRawBits, boolean isCheckWithArbitraryClasses) { try { - Verify.checkEQ(a, b); + Verify.checkEQ(a, b, isFloatCheckWithRawBits, isCheckWithArbitraryClasses); throw new RuntimeException("Should have thrown"); } catch (VerifyException e) {} } + + public static void checkNE(Object a, Object b) { + checkNE(a, b, false, false); + } } From 7b92aba96dfef9716945155d1d2f9c3dd03247f8 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 19 Mar 2025 09:51:05 +0100 Subject: [PATCH 189/212] stub arbitrary classes --- .../jtreg/compiler/lib/verify/Verify.java | 56 +++++++++++-------- .../verify/tests/TestVerify.java | 27 +++++++++ 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index 5fa81c50fa940..4601b9fba6b3b 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -87,7 +87,7 @@ private void checkEQdispatch(Object a, Object b, String field, Object aParent, O // Null mismatch if (a == null || b == null) { System.err.println("ERROR: Verify.checkEQ failed: null mismatch"); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Object array null mismatch."); } @@ -97,7 +97,7 @@ private void checkEQdispatch(Object a, Object b, String field, Object aParent, O if (ca != cb) { System.err.println("ERROR: Verify.checkEQ failed: class mismatch."); System.err.println(" " + ca.getName() + " vs " + cb.getName()); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Object class mismatch."); } @@ -148,9 +148,14 @@ private void checkEQdispatch(Object a, Object b, String field, Object aParent, O return; } - System.err.println("ERROR: Verify.checkEQ failed: type not supported: " + ca.getName()); - printX(a, b, field, aParent, bParent); - throw new VerifyException("Object type not supported: " + ca.getName()); + if (isCheckWithArbitraryClasses) { + checkEQArbitraryClasses(a, b, field, aParent, bParent); + return; + } else { + System.err.println("ERROR: Verify.checkEQ failed: type not supported: " + ca.getName()); + print(a, b, field, aParent, bParent); + throw new VerifyException("Object type not supported: " + ca.getName()); + } } } } @@ -161,7 +166,7 @@ private void checkEQdispatch(Object a, Object b, String field, Object aParent, O private void checkEQimpl(byte a, byte b, String field, Object aParent, Object bParent) { if (a != b) { System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + a + " vs " + b); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } @@ -172,7 +177,7 @@ private void checkEQimpl(byte a, byte b, String field, Object aParent, Object bP private void checkEQimpl(char a, char b, String field, Object aParent, Object bParent) { if (a != b) { System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + (int)a + " vs " + (int)b); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + (int)a + " vs " + (int)b); } } @@ -183,7 +188,7 @@ private void checkEQimpl(char a, char b, String field, Object aParent, Object bP private void checkEQimpl(short a, short b, String field, Object aParent, Object bParent) { if (a != b) { System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + (int)a + " vs " + (int)b); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + (int)a + " vs " + (int)b); } } @@ -194,7 +199,7 @@ private void checkEQimpl(short a, short b, String field, Object aParent, Object private void checkEQimpl(int a, int b, String field, Object aParent, Object bParent) { if (a != b) { System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + a + " vs " + b); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } @@ -205,7 +210,7 @@ private void checkEQimpl(int a, int b, String field, Object aParent, Object bPar private void checkEQimpl(long a, long b, String field, Object aParent, Object bParent) { if (a != b) { System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + a + " vs " + b); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } @@ -241,7 +246,7 @@ private void checkEQimpl(float a, float b, String field, Object aParent, Object System.err.println("ERROR: Verify.checkEQ failed: value mismatch. check raw: " + isFloatCheckWithRawBits); System.err.println(" Values: " + a + " vs " + b); System.err.println(" Raw: " + Float.floatToRawIntBits(a) + " vs " + Float.floatToRawIntBits(b)); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } @@ -254,7 +259,7 @@ private void checkEQimpl(double a, double b, String field, Object aParent, Objec System.err.println("ERROR: Verify.checkEQ failed: value mismatch. check raw: " + isFloatCheckWithRawBits); System.err.println(" Values: " + a + " vs " + b); System.err.println(" Raw: " + Double.doubleToRawLongBits(a) + " vs " + Double.doubleToRawLongBits(b)); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } @@ -265,7 +270,7 @@ private void checkEQimpl(double a, double b, String field, Object aParent, Objec private void checkEQimpl(boolean a, boolean b, String field, Object aParent, Object bParent) { if (a != b) { System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + a + " vs " + b); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } @@ -281,7 +286,7 @@ private void checkEQimpl(MemorySegment a, MemorySegment b, String field, Object // Print some general info System.err.println("ERROR: Verify.checkEQ failed"); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); printMemorySegment(a, "a"); printMemorySegment(b, "b"); @@ -312,7 +317,7 @@ private void checkEQimpl(Exception a, Exception b, String field, Object aParent, System.err.println("ERROR: Verify.checkEQ failed:"); System.out.println("a: " + a.getMessage()); System.out.println("b: " + b.getMessage()); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Exception message mismatch: " + a + " vs " + b); } @@ -357,14 +362,14 @@ private void checkEQimpl(long[] a, long[] b, String field, Object aParent, Objec private void checkEQimpl(float[] a, float[] b, String field, Object aParent, Object bParent) { if (a.length != b.length) { System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length + " vs " + b.length); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Float array length mismatch."); } for (int i = 0; i < a.length; i++) { if (isFloatEQ(a[i], b[i])) { System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i] + ". check raw: " + isFloatCheckWithRawBits); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Float array value mismatch " + a[i] + " vs " + b[i]); } } @@ -376,14 +381,14 @@ private void checkEQimpl(float[] a, float[] b, String field, Object aParent, Obj private void checkEQimpl(double[] a, double[] b, String field, Object aParent, Object bParent) { if (a.length != b.length) { System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length + " vs " + b.length); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Double array length mismatch."); } for (int i = 0; i < a.length; i++) { if (isDoubleEQ(a[i], b[i])) { System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i] + ". check raw: " + isFloatCheckWithRawBits); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Double array value mismatch " + a[i] + " vs " + b[i]); } } @@ -395,14 +400,14 @@ private void checkEQimpl(double[] a, double[] b, String field, Object aParent, O private void checkEQimpl(boolean[] a, boolean[] b, String field, Object aParent, Object bParent) { if (a.length != b.length) { System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length + " vs " + b.length); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Boolean array length mismatch."); } for (int i = 0; i < a.length; i++) { if (a[i] != b[i]) { System.err.println("ERROR: Verify.checkEQ failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i]); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Boolean array value mismatch."); } } @@ -416,7 +421,7 @@ private void checkEQimpl(Object[] a, Object[] b, String field, Object aParent, O // (1) Length mismatch if (a.length != b.length) { System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length + " vs " + b.length); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); throw new VerifyException("Object array length mismatch."); } @@ -426,7 +431,10 @@ private void checkEQimpl(Object[] a, Object[] b, String field, Object aParent, O } } - private void printX(Object a, Object b, String field, Object aParent, Object bParent) { + private void checkEQArbitraryClasses(Object a, Object b, String field, Object aParent, Object bParent) { + } + + private void print(Object a, Object b, String field, Object aParent, Object bParent) { System.err.println(" aParent: " + aParent); System.err.println(" bParent: " + bParent); System.err.println(" field: " + field); @@ -448,7 +456,7 @@ private boolean checkAlreadyVisited(Object a, Object b, String field, Object aPa Object bPrevious = a2b.get(a); if (b != bPrevious) { System.err.println("ERROR: Verify.checkEQ failed:"); - printX(a, b, field, aParent, bParent); + print(a, b, field, aParent, bParent); System.err.println(" bPrevious: " + bPrevious); throw new VerifyException("Mismatch with previous pair."); } diff --git a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java index 08b08597f415e..5556437403575 100644 --- a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java +++ b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java @@ -56,6 +56,8 @@ public static void main(String[] args) { // Test recursive data: Object array of values, etc. testRecursive(); + + testArbitraryClasses(); } public static void testArrayByte() { @@ -470,6 +472,31 @@ public static void testRecursive() { } } + static class A {} + + static class B {} + + static class C extends B {} + + static class D { + private int x; + } + + static class E { + private int x; + } + + public static void testArbitraryClasses() { + A a1 = new A(); + A a2 = new A(); + + // Throws exception because arbitrary classes are not allowed. + checkNE(a1, a1, false, false); + + Verify.checkEQ(a1, a1, false, true); + Verify.checkEQ(a1, a2, false, true); + } + public static void checkNE(Object a, Object b, boolean isFloatCheckWithRawBits, boolean isCheckWithArbitraryClasses) { try { Verify.checkEQ(a, b, isFloatCheckWithRawBits, isCheckWithArbitraryClasses); From 4fa0a8805f300659c084884ece73fae732b05880 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 19 Mar 2025 14:09:38 +0100 Subject: [PATCH 190/212] wip arbitrary classes --- .../jtreg/compiler/lib/verify/Verify.java | 17 +++++++- .../verify/tests/TestVerify.java | 41 ++++++++++++++++++- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index 4601b9fba6b3b..1fdd5d37a0ff0 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -26,6 +26,7 @@ import java.util.Optional; import java.lang.foreign.*; import java.lang.reflect.Method; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.ArrayList; @@ -149,7 +150,7 @@ private void checkEQdispatch(Object a, Object b, String field, Object aParent, O } if (isCheckWithArbitraryClasses) { - checkEQArbitraryClasses(a, b, field, aParent, bParent); + checkEQArbitraryClasses(a, b); return; } else { System.err.println("ERROR: Verify.checkEQ failed: type not supported: " + ca.getName()); @@ -431,7 +432,19 @@ private void checkEQimpl(Object[] a, Object[] b, String field, Object aParent, O } } - private void checkEQArbitraryClasses(Object a, Object b, String field, Object aParent, Object bParent) { + private void checkEQArbitraryClasses(Object a, Object b) { + for (Field field : a.getClass().getDeclaredFields()) { + Object va = null; + Object vb = null; + try { + field.setAccessible(true); + va = field.get(a); + vb = field.get(b); + } catch (IllegalAccessException e) { + throw new VerifyException("Failure to access field: " + field + " of " + a); + } + checkEQdispatch(va, vb, field.getName(), a, b); + } } private void print(Object a, Object b, String field, Object aParent, Object bParent) { diff --git a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java index 5556437403575..9e8261a257f69 100644 --- a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java +++ b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java @@ -479,28 +479,65 @@ static class B {} static class C extends B {} static class D { + D(int x) { + this.x = x; + } + private int x; } static class E { - private int x; + private D d; + private E e1; + private E e2; } public static void testArbitraryClasses() { A a1 = new A(); A a2 = new A(); + B b1 = new B(); + B b2 = new B(); + C c1 = new C(); + C c2 = new C(); // Throws exception because arbitrary classes are not allowed. checkNE(a1, a1, false, false); + // Structurally equivalent. Verify.checkEQ(a1, a1, false, true); Verify.checkEQ(a1, a2, false, true); + Verify.checkEQ(b1, b1, false, true); + Verify.checkEQ(b1, b2, false, true); + Verify.checkEQ(c1, c1, false, true); + Verify.checkEQ(c1, c2, false, true); + + // Must fail because of different classes. + checkNE(a1, b1, false, true); + checkNE(b1, a1, false, true); + checkNE(a1, c1, false, true); + checkNE(c1, a1, false, true); + checkNE(b1, c1, false, true); + checkNE(c1, b1, false, true); + + // Objects with primitive values. + D d1 = new D(1); + D d2 = new D(1); + D d3 = new D(2); + Verify.checkEQ(d1, d1, false, true); + Verify.checkEQ(d1, d2, false, true); + Verify.checkEQ(d2, d1, false, true); + checkNE(d1, d3, false, true); + checkNE(d3, d1, false, true); + + E e1 = new E(); + + // TODO: records! } public static void checkNE(Object a, Object b, boolean isFloatCheckWithRawBits, boolean isCheckWithArbitraryClasses) { try { Verify.checkEQ(a, b, isFloatCheckWithRawBits, isCheckWithArbitraryClasses); - throw new RuntimeException("Should have thrown"); + throw new RuntimeException("Should have thrown: " + a + " vs " + b); } catch (VerifyException e) {} } From e49815c4618010365c3282ced4f314390ca55247 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 19 Mar 2025 14:23:57 +0100 Subject: [PATCH 191/212] wip --- .../jtreg/compiler/lib/verify/Verify.java | 16 ++++----- .../verify/tests/TestVerify.java | 33 ++++++++++++++++--- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index 1fdd5d37a0ff0..e9582353211b6 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -50,8 +50,7 @@ public final class Verify { private final boolean isFloatCheckWithRawBits; private final boolean isCheckWithArbitraryClasses; private final HashMap a2b = new HashMap<>(); - private final HashMap a2id = new HashMap<>(); - private final ArrayList id2a = new ArrayList<>(); // TODO: remove? + private final HashMap b2a = new HashMap<>(); private Verify(boolean isFloatCheckWithRawBits, boolean isCheckWithArbitraryClasses) { this.isFloatCheckWithRawBits = isFloatCheckWithRawBits; @@ -457,19 +456,18 @@ private void print(Object a, Object b, String field, Object aParent, Object bPar private boolean checkAlreadyVisited(Object a, Object b, String field, Object aParent, Object bParent) { // TODO: must also check reverse direction? - Integer id = a2id.get(a); - if (id == null) { + Object bPrevious = a2b.get(a); + Object aPrevious = b2a.get(b); + if (aPrevious == null && bPrevious == null) { // Record for next time. - id = id2a.size(); - a2id.put(a, id); a2b.put(a, b); - id2a.add(a); + b2a.put(b, a); return false; } else { - Object bPrevious = a2b.get(a); - if (b != bPrevious) { + if (a != aPrevious || b != bPrevious) { System.err.println("ERROR: Verify.checkEQ failed:"); print(a, b, field, aParent, bParent); + System.err.println(" aPrevious: " + aPrevious); System.err.println(" bPrevious: " + bPrevious); throw new VerifyException("Mismatch with previous pair."); } diff --git a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java index 9e8261a257f69..2a5d23e912eb7 100644 --- a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java +++ b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java @@ -462,13 +462,13 @@ public static void testRecursive() { int v1 = (int)RANDOM.nextInt(); int v2 = (int)(v1 ^ (1 << RANDOM.nextInt(32))); checkNE(v1, v2); - checkNE(Float.intBitsToFloat(v1), Float.intBitsToFloat(v2)); + checkNE(Float.intBitsToFloat(v1), Float.intBitsToFloat(v2), true, false); } for (int i = 0; i < 10; i++) { long v1 = (long)RANDOM.nextLong(); long v2 = (long)(v1 ^ (1L << RANDOM.nextInt(64))); checkNE(v1, v2); - checkNE(Double.longBitsToDouble(v1), Double.longBitsToDouble(v2)); + checkNE(Double.longBitsToDouble(v1), Double.longBitsToDouble(v2), true, false); } } @@ -487,9 +487,15 @@ static class D { } static class E { + E(D d, E e1, E e2) { + this.d = d; + this.e1 = e1; + this.e2 = e2; + } + private D d; - private E e1; - private E e2; + public E e1; + public E e2; } public static void testArbitraryClasses() { @@ -529,7 +535,24 @@ public static void testArbitraryClasses() { checkNE(d1, d3, false, true); checkNE(d3, d1, false, true); - E e1 = new E(); + E e1 = new E(d1, null, null); + E e2 = new E(d1, null, null); + E e3 = new E(d3, null, null); + E e4 = new E(d1, e1, null); + E e5 = new E(d1, e2, null); + E e6 = new E(d1, null, null); + e6.e1 = e6; + E e7 = new E(d1, null, null); + e7.e1 = e7; + + Verify.checkEQ(e1, e1, false, true); + Verify.checkEQ(e1, e2, false, true); + Verify.checkEQ(e2, e1, false, true); + checkNE(e1, e3, false, true); + checkNE(e3, e1, false, true); + Verify.checkEQ(e6, e6, false, true); + Verify.checkEQ(e6, e7, false, true); + Verify.checkEQ(e7, e6, false, true); // TODO: records! } From 765349ec99ea7dceb104e62930a515b1972f2a33 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 19 Mar 2025 14:43:54 +0100 Subject: [PATCH 192/212] classes with extends --- .../jtreg/compiler/lib/verify/Verify.java | 26 +++++++----- .../verify/tests/TestVerify.java | 40 +++++++++++++++++++ 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index e9582353211b6..26dc8d2381241 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -432,17 +432,22 @@ private void checkEQimpl(Object[] a, Object[] b, String field, Object aParent, O } private void checkEQArbitraryClasses(Object a, Object b) { - for (Field field : a.getClass().getDeclaredFields()) { - Object va = null; - Object vb = null; - try { - field.setAccessible(true); - va = field.get(a); - vb = field.get(b); - } catch (IllegalAccessException e) { - throw new VerifyException("Failure to access field: " + field + " of " + a); + Class c = a.getClass(); + while (c != Object.class) { + for (Field field : c.getDeclaredFields()) { + System.out.println(field); + Object va = null; + Object vb = null; + try { + field.setAccessible(true); + va = field.get(a); + vb = field.get(b); + } catch (IllegalAccessException e) { + throw new VerifyException("Failure to access field: " + field + " of " + a); + } + checkEQdispatch(va, vb, field.getName(), a, b); } - checkEQdispatch(va, vb, field.getName(), a, b); + c = c.getSuperclass(); } } @@ -455,7 +460,6 @@ private void print(Object a, Object b, String field, Object aParent, Object bPar } private boolean checkAlreadyVisited(Object a, Object b, String field, Object aParent, Object bParent) { - // TODO: must also check reverse direction? Object bPrevious = a2b.get(a); Object aPrevious = b2a.get(b); if (aPrevious == null && bPrevious == null) { diff --git a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java index 2a5d23e912eb7..9810555bbf2ce 100644 --- a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java +++ b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java @@ -498,6 +498,24 @@ static class E { public E e2; } + static class F { + private int x; + + public F(int x) { + this.x = x; + } + } + + static class F2 extends F { + private int y; + + F2(int x, int y) { + super(x); + this.y = y; + } + } + + public static void testArbitraryClasses() { A a1 = new A(); A a2 = new A(); @@ -535,6 +553,7 @@ public static void testArbitraryClasses() { checkNE(d1, d3, false, true); checkNE(d3, d1, false, true); + // Object fields, including cycles. E e1 = new E(d1, null, null); E e2 = new E(d1, null, null); E e3 = new E(d3, null, null); @@ -544,6 +563,8 @@ public static void testArbitraryClasses() { e6.e1 = e6; E e7 = new E(d1, null, null); e7.e1 = e7; + E e8 = new E(d1, e1, e1); + E e9 = new E(d1, e1, e2); Verify.checkEQ(e1, e1, false, true); Verify.checkEQ(e1, e2, false, true); @@ -553,6 +574,25 @@ public static void testArbitraryClasses() { Verify.checkEQ(e6, e6, false, true); Verify.checkEQ(e6, e7, false, true); Verify.checkEQ(e7, e6, false, true); + Verify.checkEQ(e8, e8, false, true); + checkNE(e8, e9, false, true); + checkNE(e9, e8, false, true); + + // Fields from superclass. + F2 f1 = new F2(1, 1); + F2 f2 = new F2(1, 1); + F2 f3 = new F2(2, 1); + F2 f4 = new F2(1, 2); + + Verify.checkEQ(f1, f1, false, true); + Verify.checkEQ(f1, f2, false, true); + Verify.checkEQ(f2, f1, false, true); + checkNE(f1, f3, false, true); + checkNE(f1, f4, false, true); + checkNE(f3, f1, false, true); + checkNE(f4, f1, false, true); + checkNE(f3, f4, false, true); + checkNE(f4, f3, false, true); // TODO: records! } From 5edfa616ba6eb3b29c9f992486292d7869d8bfa1 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 19 Mar 2025 14:57:31 +0100 Subject: [PATCH 193/212] records --- .../verify/tests/TestVerify.java | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java index 9810555bbf2ce..a91101e5cb5cf 100644 --- a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java +++ b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java @@ -515,6 +515,10 @@ static class F2 extends F { } } + static record R1() {} + static record R2() {} + static record R3(int x, int y) {} + static record R4(R4 x, R4 y) {} public static void testArbitraryClasses() { A a1 = new A(); @@ -594,7 +598,42 @@ public static void testArbitraryClasses() { checkNE(f3, f4, false, true); checkNE(f4, f3, false, true); - // TODO: records! + // Records. + R1 r11 = new R1(); + R1 r12 = new R1(); + R2 r21 = new R2(); + R3 r31 = new R3(1, 1); + R3 r32 = new R3(1, 1); + R3 r33 = new R3(1, 2); + R3 r34 = new R3(2, 1); + + Verify.checkEQ(r11, r11, false, true); + Verify.checkEQ(r11, r12, false, true); + Verify.checkEQ(r12, r11, false, true); + checkNE(r11, r21, false, true); + Verify.checkEQ(r31, r31, false, true); + Verify.checkEQ(r31, r32, false, true); + Verify.checkEQ(r32, r31, false, true); + checkNE(r31, r33, false, true); + checkNE(r33, r31, false, true); + checkNE(r31, r34, false, true); + checkNE(r34, r31, false, true); + checkNE(r33, r34, false, true); + + R4 r41 = new R4(null, null); + R4 r42 = new R4(null, null); + R4 r43 = new R4(r41, null); + R4 r44 = new R4(r42, null); + R4 r45 = new R4(r43, r41); + R4 r46 = new R4(r44, r42); + R4 r47 = new R4(r44, r41); + + Verify.checkEQ(r45, r46, false, true); + Verify.checkEQ(r46, r45, false, true); + checkNE(r45, r47, false, true); + checkNE(r47, r45, false, true); + checkNE(r46, r47, false, true); + checkNE(r47, r46, false, true); } public static void checkNE(Object a, Object b, boolean isFloatCheckWithRawBits, boolean isCheckWithArbitraryClasses) { From e1c940e93d3aa553365f6d9106e88894fc41ec57 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 19 Mar 2025 15:14:04 +0100 Subject: [PATCH 194/212] improve documentation --- .../jtreg/compiler/lib/verify/Verify.java | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index 26dc8d2381241..554ec07230c35 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -33,20 +33,30 @@ /** - * TODO: update description - * The {@link Verify} class provides a single {@link Verify#checkEQ} static method, which recursively - * compares the two {@link Object}s by value. It deconstructs {@link Object[]}, compares boxed primitive - * types, compares the content of arrays and {@link MemorySegment}s, and checks that the messages of two - * {@link Exception}s are equal. We also check for equivalent content in {@link Vector}s from the Vector - * API. - * - * TODO: mention that MemorySegment is always checked raw, also for floats. + * The {@link Verify} class provides {@link Verify#checkEQ}, which recursively compares the two + * {@link Object}s by value. It deconstructs {@link Object[]}, compares boxed primitive types, + * compares the content of arrays and {@link MemorySegment}s, and checks that the messages of two + * {@link Exception}s are equal. It also checks for the equivalent content in {@code Vector}s from + * the Vector API. * + *

* When a comparison fail, then methods print helpful messages, before throwing a {@link VerifyException}. + * + *

+ * We have to take special care of {@link Float}s and {@link Double}s, since they have both various + * encodings for NaN values, but on Java specification they are to be regarded as equal. Hence, we + * have two modes of comparison, specified by {@code isFloatCheckWithRawBits}. By default, it is + * disabled, and different NaN values are regarded as equal. This applies to the boxed floating types, + * as well as arrays of floating arrays. When {@code isFloatCheckWithRawBits} is enabled, we compare + * the raw bits, and so different NaN encodings are not equal. Note: {@link MemorySegment} data is + * always compared with raw bits. + * + *

+ * By default, we only support comparison of the types mentioned above. However, in some cases one + * might want to compare Objects of arbitrare classes by value, i.e. the recursive structure given + * by their field values. This feature can be enabled with {@code isCheckWithArbitraryClasses}. */ public final class Verify { - - // TODO: fields for float exactness, maps, etc. private final boolean isFloatCheckWithRawBits; private final boolean isCheckWithArbitraryClasses; private final HashMap a2b = new HashMap<>(); @@ -57,18 +67,24 @@ private Verify(boolean isFloatCheckWithRawBits, boolean isCheckWithArbitraryClas this.isCheckWithArbitraryClasses = isCheckWithArbitraryClasses; } - // TODO: desc + /** + * Verify the content of two Objects, possibly recursively. + * + * @param a First object to be recursively compared with the second. + * @param b Second object to be recursively compared with the first. + * @param isFloatCheckWithRawBits Determines if different NaN encodings are equal or not. + * @param isCheckWithArbitraryClasses Determines if the structural comparison of Objects from arbitrary classes is enabled. + * @throws VerifyException If the comparison fails. + */ public static void checkEQ(Object a, Object b, boolean isFloatCheckWithRawBits, boolean isCheckWithArbitraryClasses) { Verify v = new Verify(isFloatCheckWithRawBits, isCheckWithArbitraryClasses); v.checkEQdispatch(a, b, "", null, null); } - // recursive, so that we have nicer stack trace? - no need for map! - // queue: allows deeper structures - // We need to think about "edges": (a, b) -field-> (c, d) - /** - * Verify the content of two Objects, possibly recursively. Only limited types are implemented. + * Verify the content of two Objects, possibly recursively. + * Only limited types are implemented (no arbitrary classes). + * Different NaN encodins are considered equal. * * @param a First object to be recursively compared with the second. * @param b Second object to be recursively compared with the first. From b29a2d29f2752913f37cced39cb58b6d55eec473 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 19 Mar 2025 15:22:56 +0100 Subject: [PATCH 195/212] fix verify usage --- test/hotspot/jtreg/compiler/lib/verify/Verify.java | 2 +- .../template_library/examples/TestFuzzClasses.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index 554ec07230c35..5f735d02664dc 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -170,7 +170,7 @@ private void checkEQdispatch(Object a, Object b, String field, Object aParent, O } else { System.err.println("ERROR: Verify.checkEQ failed: type not supported: " + ca.getName()); print(a, b, field, aParent, bParent); - throw new VerifyException("Object type not supported: " + ca.getName()); + throw new VerifyException("Object type not supported: " + ca.getName() + " -- did you mean to enable 'isCheckWithArbitraryClasses'?"); } } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java index 59dfd4464de50..e323812732497 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java @@ -121,7 +121,7 @@ public static class #classType { @Check(test = "$test") public static void $check(Object result) { - Verify.checkEQ(result, $GOLD); + Verify.checkEQ(result, $GOLD, false, true); } // --- $test end --- From 6d4c1b61bb7b69d482e56b74cc5069b31d2466b8 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 20 Mar 2025 07:23:37 +0100 Subject: [PATCH 196/212] wip class template, and fix verify NaN issue --- .../lib/template_library/ClassType.java | 60 +++++++++++++++++-- .../lib/template_library/Library.java | 2 +- .../jtreg/compiler/lib/verify/Verify.java | 9 ++- .../examples/TestFuzzClasses.java | 21 +++---- .../verify/tests/TestVerify.java | 24 ++++++++ 5 files changed, 100 insertions(+), 16 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java b/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java index 258b52ff60518..4ee1439bd645b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java @@ -23,19 +23,39 @@ package compiler.lib.template_library; +import java.util.List; +import java.util.ArrayList; + +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateWithArgs; import compiler.lib.template_framework.Name; +import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.let; +// TODO: desc +// simplicity: all fields are public and non-static. public final class ClassType extends Type { + public final ClassType superClass; public final String className; - public ClassType(String className) { + public static record ClassField(String name, Type type) {} + protected final List fields = new ArrayList(); + + public ClassType(String className, ClassType superClass) { this.className = className; + this.superClass = superClass; } @Override public boolean isSubtypeOf(Name.Type other) { - // TODO: subtyping? - return this == other; + ClassType ct = this; + while (ct != null) { + if (ct == other) { + return true; + } + ct = ct.superClass; + } + return false; } @Override @@ -46,5 +66,37 @@ public final Object con() { return "new " + className + "()"; } - // TODO: template + public final void addField(String name, Type type) { + fields.add(new ClassField(name, type)); + } + + public final List allFields() { + List list = new ArrayList<>(); + ClassType ct = this; + while (ct != null) { + list.addAll(ct.fields); + ct = ct.superClass; + } + return list; + } + + public TemplateWithArgs templateWithArgs() { + var fieldTemplate = Template.make("cf", (ClassField cf) -> body( + let("name", cf.name()), + let("type", cf.type()), + "public #type #name = ", cf.type().con(), ";\n" + )); + var template = Template.make(() -> body( + let("class", className), + """ + public static class #class { + """, + fields.stream().map((ClassField cf) -> fieldTemplate.withArgs(cf)).toList(), + """ + public #class() {} + } + """ + )); + return template.withArgs(); + } } diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Library.java b/test/hotspot/jtreg/compiler/lib/template_library/Library.java index 8610d4cad6c4b..6c480e6aff557 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Library.java @@ -50,7 +50,7 @@ public abstract class Library { public static final Hook CLASS_HOOK = new Hook("Class"); public static final Hook METHOD_HOOK = new Hook("Method"); - static T choice(List list) { + public static T choice(List list) { if (list.isEmpty()) { return null; } int i = RANDOM.nextInt(list.size()); return list.get(i); diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index 5f735d02664dc..d18cdf1dac23e 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -117,7 +117,8 @@ private void checkEQdispatch(Object a, Object b, String field, Object aParent, O throw new VerifyException("Object class mismatch."); } - // Already visited? + // Already visited? This makes sure that we are not stuck in cycles, and that we have + // a mapping of pairs (a, b) for structurally equivalent Objects. if (checkAlreadyVisited(a, b, field, aParent, bParent)) { return; } @@ -476,6 +477,12 @@ private void print(Object a, Object b, String field, Object aParent, Object bPar } private boolean checkAlreadyVisited(Object a, Object b, String field, Object aParent, Object bParent) { + // Floating numbers would fail the "==" check below, and it is not very useful to map their + // pairs anyway, as repeated comparison is cheap. + if (a instanceof Float || a instanceof Double) { + return false; + } + Object bPrevious = a2b.get(a); Object aPrevious = b2a.get(b); if (aPrevious == null && bPrevious == null) { diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java index e323812732497..faca524e5b347 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java @@ -37,6 +37,9 @@ import java.util.List; import java.util.ArrayList; +import java.util.Random; +import jdk.test.lib.Utils; + import compiler.lib.compile_framework.CompileFramework; import compiler.lib.template_framework.Template; @@ -55,6 +58,7 @@ * See {@link TestFuzzExpression} for more explanation about the structure of the test. */ public class TestFuzzClasses { + private static final Random RANDOM = Utils.getRandomInstance(); public static void main(String[] args) { // Create a new CompileFramework instance. @@ -81,17 +85,14 @@ public static String generate(CompileFramework comp) { "compiler.lib.verify.*"), List.of()); - var classTemplate = Template.make("classType", (ClassType classType) -> body( - """ - public static class #classType { - public #classType() {} - } - """ - )); - ArrayList classTypes = new ArrayList<>(); for (int i = 0; i < 10; i++) { - classTypes.add(new ClassType("C" + i)); + ClassType ct = new ClassType("C" + i, null); + classTypes.add(ct); + for (int j = 0; j < RANDOM.nextInt(10); j++) { + Type type = Library.choice(Type.PRIMITIVE_TYPES); + ct.addField("f_" + i + "_" + j, type); + } } // TODO: description @@ -132,7 +133,7 @@ public static class #classType { // Now use the templates and add them into the IRTestClass. List templates = new ArrayList<>(); for (var classType : classTypes) { - templates.add(classTemplate.withArgs(classType)); + templates.add(classType.templateWithArgs()); } for (var classType : classTypes) { templates.add(template1.withArgs(classType)); diff --git a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java index a91101e5cb5cf..3fdc89c553a68 100644 --- a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java +++ b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java @@ -515,6 +515,16 @@ static class F2 extends F { } } + static class G { + private float x; + private float y; + + public G(float x, float y) { + this.x = x; + this.y = y; + } + } + static record R1() {} static record R2() {} static record R3(int x, int y) {} @@ -598,6 +608,20 @@ public static void testArbitraryClasses() { checkNE(f3, f4, false, true); checkNE(f4, f3, false, true); + G g1 = new G(1.0f, 1.0f); + G g2 = new G(1.0f, 1.0f); + G g3 = new G(Float.NaN, Float.NaN); + G g4 = new G(Float.NaN, Float.NaN); + + Verify.checkEQ(g1, g1, false, true); + Verify.checkEQ(g2, g1, false, true); + Verify.checkEQ(g1, g2, false, true); + Verify.checkEQ(g3, g3, false, true); + Verify.checkEQ(g3, g4, false, true); + Verify.checkEQ(g4, g3, false, true); + checkNE(g1, g3, false, true); + checkNE(g3, g1, false, true); + // Records. R1 r11 = new R1(); R1 r12 = new R1(); From 5911fe2c565004fec9fa409f4b4c7a83eb3468ee Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 20 Mar 2025 07:31:47 +0100 Subject: [PATCH 197/212] rm print --- test/hotspot/jtreg/compiler/lib/verify/Verify.java | 1 - 1 file changed, 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index d18cdf1dac23e..e436e2b45ab51 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -452,7 +452,6 @@ private void checkEQArbitraryClasses(Object a, Object b) { Class c = a.getClass(); while (c != Object.class) { for (Field field : c.getDeclaredFields()) { - System.out.println(field); Object va = null; Object vb = null; try { From 81a012bb0c0a5b9d0a25841ec8a4a3d862141910 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 20 Mar 2025 09:11:27 +0100 Subject: [PATCH 198/212] class constructors and subclasses --- .../lib/template_library/ClassType.java | 23 +++++++++++++++---- .../lib/template_library/Library.java | 2 +- .../lib/template_library/Operations.java | 2 +- .../examples/TestFuzzClasses.java | 5 ++-- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java b/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java index 4ee1439bd645b..5041107ea495f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java @@ -70,7 +70,7 @@ public final void addField(String name, Type type) { fields.add(new ClassField(name, type)); } - public final List allFields() { + public final List collectAllFields() { List list = new ArrayList<>(); ClassType ct = this; while (ct != null) { @@ -80,20 +80,33 @@ public final List allFields() { return list; } - public TemplateWithArgs templateWithArgs() { + // TODO: template that executes some random statement: create, modify, etc... + + public TemplateWithArgs classDefinitionTemplateWithArgs() { + List allFields = collectAllFields(); + var fieldTemplate = Template.make("cf", (ClassField cf) -> body( let("name", cf.name()), let("type", cf.type()), "public #type #name = ", cf.type().con(), ";\n" )); + var template = Template.make(() -> body( let("class", className), + // Declaring the class. + "public static class #class ", (superClass == null ? "" : "extends " + superClass) ," {\n", + // Define local fields. + fields.stream().map((ClassField cf) -> fieldTemplate.withArgs(cf)).toList(), + // Constructor with arguments for every field. + "public #class(", String.join(", ", allFields.stream().map((ClassField cf) -> cf.type() + " " + cf.name()).toList()), ") {\n", + allFields.stream().map((ClassField cf) -> " this." + cf.name() + " = " + cf.name() + ";\n").toList(), """ - public static class #class { + } """, - fields.stream().map((ClassField cf) -> fieldTemplate.withArgs(cf)).toList(), + // Default constructor. + allFields.size() == 0 ? "" + : "public #class() {} // default\n", """ - public #class() {} } """ )); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Library.java b/test/hotspot/jtreg/compiler/lib/template_library/Library.java index 6c480e6aff557..07f7ee32ede87 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Library.java @@ -56,7 +56,7 @@ public static T choice(List list) { return list.get(i); } - static final List concat(List ... lists) { + public static final List concat(List ... lists) { List list = new ArrayList(); for (var l : lists) { list.addAll(l); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index d7cb1c35e587a..fa76be90450a1 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -28,7 +28,7 @@ import java.util.stream.Stream; // TODO: find more operations, like in Math.java or ExactMath.java -final class Operations { +public final class Operations { private static final List BYTE_OPERATIONS = List.of( // Note: the standard integer arithmetic operations are only defined for int/long. diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java index faca524e5b347..ef89f2149866d 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java @@ -87,7 +87,8 @@ public static String generate(CompileFramework comp) { ArrayList classTypes = new ArrayList<>(); for (int i = 0; i < 10; i++) { - ClassType ct = new ClassType("C" + i, null); + ClassType superClass = i > 0 && RANDOM.nextInt(2) == 0 ? Library.choice(classTypes) : null; + ClassType ct = new ClassType("C" + i, superClass); classTypes.add(ct); for (int j = 0; j < RANDOM.nextInt(10); j++) { Type type = Library.choice(Type.PRIMITIVE_TYPES); @@ -133,7 +134,7 @@ public static String generate(CompileFramework comp) { // Now use the templates and add them into the IRTestClass. List templates = new ArrayList<>(); for (var classType : classTypes) { - templates.add(classType.templateWithArgs()); + templates.add(classType.classDefinitionTemplateWithArgs()); } for (var classType : classTypes) { templates.add(template1.withArgs(classType)); From bf774726b47c46646bffd3e6df10cf2264a8677a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 20 Mar 2025 10:49:12 +0100 Subject: [PATCH 199/212] fix issue with boxed types --- .../lib/template_library/ClassType.java | 1 + .../jtreg/compiler/lib/verify/Verify.java | 17 +++++++++++---- .../examples/TestFuzzClasses.java | 12 +++++++---- .../verify/tests/TestVerify.java | 21 +++++++++++++++++++ 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java b/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java index 5041107ea495f..1b0153f644164 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java @@ -34,6 +34,7 @@ // TODO: desc // simplicity: all fields are public and non-static. +// TODO: abstract? public final class ClassType extends Type { public final ClassType superClass; public final String className; diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index e436e2b45ab51..fc6626e89c7bc 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -476,10 +476,19 @@ private void print(Object a, Object b, String field, Object aParent, Object bPar } private boolean checkAlreadyVisited(Object a, Object b, String field, Object aParent, Object bParent) { - // Floating numbers would fail the "==" check below, and it is not very useful to map their - // pairs anyway, as repeated comparison is cheap. - if (a instanceof Float || a instanceof Double) { - return false; + // Boxed primitives are not guaranteed to be the same Object for the same primitive value. + // Hence, we cannot use the mapping below. We test these boxed primitive types by value anyway, + // and they are no recursive structures, so there is no point in optimizing here anyway. + switch(a) { + case Boolean x -> { return false; } + case Byte x -> { return false; } + case Short x -> { return false; } + case Character x -> { return false; } + case Integer x -> { return false; } + case Long x -> { return false; } + case Float x -> { return false; } + case Double x -> { return false; } + default -> {} } Object bPrevious = a2b.get(a); diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java index ef89f2149866d..d574ad37242a1 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzClasses.java @@ -86,14 +86,18 @@ public static String generate(CompileFramework comp) { List.of()); ArrayList classTypes = new ArrayList<>(); + ArrayList fieldTypes = new ArrayList<>(); + fieldTypes.addAll(Type.PRIMITIVE_TYPES); for (int i = 0; i < 10; i++) { ClassType superClass = i > 0 && RANDOM.nextInt(2) == 0 ? Library.choice(classTypes) : null; - ClassType ct = new ClassType("C" + i, superClass); - classTypes.add(ct); + ClassType classType = new ClassType("C" + i, superClass); + classTypes.add(classType); for (int j = 0; j < RANDOM.nextInt(10); j++) { - Type type = Library.choice(Type.PRIMITIVE_TYPES); - ct.addField("f_" + i + "_" + j, type); + Type type = Library.choice(fieldTypes); + classType.addField("f_" + classType.name() + "_" + j, type); } + // Only add the type to the field types after we create the fields, to prevent cycles. + fieldTypes.add(classType); } // TODO: description diff --git a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java index 3fdc89c553a68..5184ed6a28406 100644 --- a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java +++ b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java @@ -525,6 +525,23 @@ public G(float x, float y) { } } + public static class H1 { + public boolean bool = true; + public byte b = (byte)242; + public short s = (short)24242; + public char c = (char)24242; + public int i = 1335836768; + public long l = 4242424242L; + public float f = 42.0f; + public double d = 42.0; + public H1() {} + } + + public static class H2 extends H1 { + public H1 h1 = new H1(); + public H2() {} + } + static record R1() {} static record R2() {} static record R3(int x, int y) {} @@ -622,6 +639,10 @@ public static void testArbitraryClasses() { checkNE(g1, g3, false, true); checkNE(g3, g1, false, true); + // Nested class with primitive types, where the boxed types may not be cached, + // and so they would create different boxed objects. + Verify.checkEQ(new H2(), new H2(), false, true); + // Records. R1 r11 = new R1(); R1 r12 = new R1(); From 69b118538eabd1ec6460699130c9e9db4c1ca6b4 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 20 Mar 2025 15:20:07 +0100 Subject: [PATCH 200/212] stub random code test --- .../lib/template_library/ClassType.java | 5 + .../lib/template_library/Library.java | 2 + .../examples/TestFuzzCode.java | 129 ++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java diff --git a/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java b/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java index 1b0153f644164..3e45cb7670806 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/ClassType.java @@ -82,6 +82,11 @@ public final List collectAllFields() { } // TODO: template that executes some random statement: create, modify, etc... + public List statementTemplates() { + return List.of( + // TODO: add cases + ); + } public TemplateWithArgs classDefinitionTemplateWithArgs() { List allFields = collectAllFields(); diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Library.java b/test/hotspot/jtreg/compiler/lib/template_library/Library.java index 07f7ee32ede87..6a5f69395ef65 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Library.java @@ -95,6 +95,8 @@ public static TemplateWithArgs defineVariableWithComputation(String name, Type t return template.withArgs(); } + // TODO: code dispatcher? + public static TemplateWithArgs arrayFillMethods() { var template = Template.make(() -> body( """ diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java new file mode 100644 index 0000000000000..cc6ee281f1c6b --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java @@ -0,0 +1,129 @@ +/* + * 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 + * @summary Test the Template Library's generation of classes. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @compile ../../../compiler/lib/ir_framework/TestFramework.java + * @compile ../../../compiler/lib/generators/Generators.java + * @compile ../../../compiler/lib/verify/Verify.java + * @run driver template_library.examples.TestFuzzCode + */ + +package template_library.examples; + +import java.util.List; +import java.util.ArrayList; + +import java.util.Random; +import jdk.test.lib.Utils; + +import compiler.lib.compile_framework.CompileFramework; + +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateWithArgs; +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_library.Library; +import compiler.lib.template_library.IRTestClass; +import compiler.lib.template_library.Type; +import compiler.lib.template_library.ClassType; + +/** + * TODO + * See {@link TestFuzzExpression} for more explanation about the structure of the test. + */ +public class TestFuzzCode { + private static final Random RANDOM = Utils.getRandomInstance(); + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("p.xyz.InnerTest", generate(comp)); + + // Compile the source file. + comp.compile(); + + // Object ret = p.xyz.InnterTest.main(); + Object ret = comp.invoke("p.xyz.InnerTest", "main", new Object[] {}); + } + + // Generate a source Java file as String + public static String generate(CompileFramework comp) { + // Create the info required for the test class. + // It is imporant that we pass the classpath to the Test-VM, so that it has access + // to all compiled classes. + IRTestClass.Info info = new IRTestClass.Info(comp.getEscapedClassPathOfCompiledClasses(), + "p.xyz", "InnerTest", + List.of("compiler.lib.generators.*", + "compiler.lib.verify.*"), + List.of()); + + var template1Body = Template.make(()-> { + return body( + // TODO: random code, then sample random variable! + "return ", Type.ints().con(), ";\n" + ); + }); + var template1 = Template.make(() -> body( + """ + // --- $test start --- + """, + Library.CLASS_HOOK.set( + """ + + static final Object $GOLD = $test(); + + @Test + public static Object $test() { + """, + Library.METHOD_HOOK.set( + template1Body.withArgs() + ), + """ + } + + @Check(test = "$test") + public static void $check(Object result) { + Verify.checkEQ(result, $GOLD, false, true); + } + + // --- $test end --- + """ + ) + )); + + // Now use the templates and add them into the IRTestClass. + List templates = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + templates.add(template1.withArgs()); + } + return IRTestClass.TEMPLATE.withArgs(info, templates).render(); + } +} From 2a3561343939511298f0b9dae3181538c7d94237 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 20 Mar 2025 15:37:07 +0100 Subject: [PATCH 201/212] wip fuzz code, return variable --- .../examples/TestFuzzCode.java | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java index cc6ee281f1c6b..45adc803b5127 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java @@ -44,14 +44,15 @@ import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateWithArgs; +import compiler.lib.template_framework.Name; import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; +import static compiler.lib.template_framework.Template.addName; import compiler.lib.template_library.Library; import compiler.lib.template_library.IRTestClass; import compiler.lib.template_library.Type; -import compiler.lib.template_library.ClassType; /** * TODO @@ -85,15 +86,18 @@ public static String generate(CompileFramework comp) { "compiler.lib.verify.*"), List.of()); - var template1Body = Template.make(()-> { - return body( - // TODO: random code, then sample random variable! - "return ", Type.ints().con(), ";\n" - ); - }); - var template1 = Template.make(() -> body( + var template1Body = Template.make("type", (Type type)-> body( + // The "ret" variable captures the return value, which can be read / modified + // by the random code. + addName(new Name($("ret"), type, true, 1)), + "#type $ret = ", type.con(), ";\n", + // TODO: random code, then sample random variable! + "return $ret;\n" + )); + var template1 = Template.make("type", (Type type) -> body( """ // --- $test start --- + // type: #type """, Library.CLASS_HOOK.set( """ @@ -104,14 +108,14 @@ public static String generate(CompileFramework comp) { public static Object $test() { """, Library.METHOD_HOOK.set( - template1Body.withArgs() + template1Body.withArgs(type) ), """ } @Check(test = "$test") public static void $check(Object result) { - Verify.checkEQ(result, $GOLD, false, true); + Verify.checkEQ(result, $GOLD); } // --- $test end --- @@ -121,8 +125,8 @@ public static String generate(CompileFramework comp) { // Now use the templates and add them into the IRTestClass. List templates = new ArrayList<>(); - for (int i = 0; i < 10; i++) { - templates.add(template1.withArgs()); + for (Type type : Type.PRIMITIVE_TYPES) { + templates.add(template1.withArgs(type)); } return IRTestClass.TEMPLATE.withArgs(info, templates).render(); } From 82ae973868d75d03ce979834f15bf1d9a776e807 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 20 Mar 2025 16:05:51 +0100 Subject: [PATCH 202/212] stub dispatcher --- .../lib/template_library/Dispatcher.java | 61 +++++++++++++++++++ .../lib/template_library/Library.java | 2 - .../examples/TestFuzzCode.java | 5 +- 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java b/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java new file mode 100644 index 0000000000000..f3a8b5109cfda --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java @@ -0,0 +1,61 @@ +/* + * 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_library; + +import java.util.List; + +// TODO: rm? +import compiler.lib.template_framework.Hook; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.Name; +import compiler.lib.template_framework.TemplateWithArgs; +import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.let; +import static compiler.lib.template_framework.Template.addName; + +/** + * TODO + */ +public class Dispatcher { + //// // Ok, now we want to add random code. We want some kind of dispatcher, that we return to + //// // all the time. Can we also have conditions for Templates, to make sure we only choose + //// // them when their condition is present? + //// // + //// // Dispatcher(list of Templates) -> chooses template, instanciates template with dispatcher. + //// // -> is this a template or something else??? + //// // -> Dispatcher object, knows what types etc allowed... hmm ok. But then it may have to + //// // create templates on the fly to make decisions based on code state? + //// public static List> basicStatements() { + //// } + + public Dispatcher() {} + + public TemplateWithArgs templateWithArgs() { + // TODO: something! + var template = Template.make(() -> body( + "// empty\n" + )); + return template.withArgs(); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Library.java b/test/hotspot/jtreg/compiler/lib/template_library/Library.java index 6a5f69395ef65..07f7ee32ede87 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Library.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Library.java @@ -95,8 +95,6 @@ public static TemplateWithArgs defineVariableWithComputation(String name, Type t return template.withArgs(); } - // TODO: code dispatcher? - public static TemplateWithArgs arrayFillMethods() { var template = Template.make(() -> body( """ diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java index 45adc803b5127..dfbad5ad5d2be 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java @@ -51,6 +51,7 @@ import static compiler.lib.template_framework.Template.addName; import compiler.lib.template_library.Library; +import compiler.lib.template_library.Dispatcher; import compiler.lib.template_library.IRTestClass; import compiler.lib.template_library.Type; @@ -89,9 +90,9 @@ public static String generate(CompileFramework comp) { var template1Body = Template.make("type", (Type type)-> body( // The "ret" variable captures the return value, which can be read / modified // by the random code. - addName(new Name($("ret"), type, true, 1)), + addName(new Name($("ret"), type, true, 10)), "#type $ret = ", type.con(), ";\n", - // TODO: random code, then sample random variable! + new Dispatcher().templateWithArgs(), "return $ret;\n" )); var template1 = Template.make("type", (Type type) -> body( From cd2f31eb70f77fedaa3708706fa466d5704f4b60 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 21 Mar 2025 09:51:52 +0100 Subject: [PATCH 203/212] basic working dispatcher --- .../lib/template_library/Dispatcher.java | 16 +++++++++-- .../examples/TestFuzzCode.java | 28 ++++++++++++++++++- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java b/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java index f3a8b5109cfda..f08fa857577b4 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java @@ -33,6 +33,8 @@ import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.addName; +import static compiler.lib.template_framework.Template.fuel; +import static compiler.lib.template_framework.Template.setFuelCost; /** * TODO @@ -49,12 +51,20 @@ public class Dispatcher { //// public static List> basicStatements() { //// } - public Dispatcher() {} + private final List> templates; - public TemplateWithArgs templateWithArgs() { + public Dispatcher(List> templates) { + this.templates = templates; + } + + public TemplateWithArgs call() { // TODO: something! var template = Template.make(() -> body( - "// empty\n" + setFuelCost(0), + let("fuel", fuel()), + "// $dispatch fuel: #fuel\n", + (fuel() <= 0) ? "// $empty\n" + : Library.choice(templates).withArgs(this) )); return template.withArgs(); } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java index dfbad5ad5d2be..c6ddecd465e78 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java @@ -87,12 +87,38 @@ public static String generate(CompileFramework comp) { "compiler.lib.verify.*"), List.of()); + + Dispatcher dispatcher = new Dispatcher(List.of( + Template.make("dispatcher", (Dispatcher d) -> body( + """ + { // x $open + """, + d.call(), + """ + } // x $close + """ + )), + Template.make("dispatcher", (Dispatcher d) -> body( + """ + { // y $open + """, + d.call(), + """ + // y $mid + """, + d.call(), + """ + } // y $close + """ + )) + )); + var template1Body = Template.make("type", (Type type)-> body( // The "ret" variable captures the return value, which can be read / modified // by the random code. addName(new Name($("ret"), type, true, 10)), "#type $ret = ", type.con(), ";\n", - new Dispatcher().templateWithArgs(), + dispatcher.call(), "return $ret;\n" )); var template1 = Template.make("type", (Type type) -> body( From f00172051ae7b8a707ff6dc3bf94506e7eef1f17 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 24 Mar 2025 13:37:03 +0100 Subject: [PATCH 204/212] fix indentation --- .../examples/TestFuzzCode.java | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java index c6ddecd465e78..a193e4511ba3c 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java @@ -49,6 +49,7 @@ import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; import static compiler.lib.template_framework.Template.addName; +import static compiler.lib.template_framework.Template.setFuelCost; import compiler.lib.template_library.Library; import compiler.lib.template_library.Dispatcher; @@ -114,6 +115,7 @@ public static String generate(CompileFramework comp) { )); var template1Body = Template.make("type", (Type type)-> body( + setFuelCost(0), // The "ret" variable captures the return value, which can be read / modified // by the random code. addName(new Name($("ret"), type, true, 10)), @@ -122,32 +124,33 @@ public static String generate(CompileFramework comp) { "return $ret;\n" )); var template1 = Template.make("type", (Type type) -> body( + setFuelCost(0), + """ + // --- $test start --- + // type: #type + """, + Library.CLASS_HOOK.set( """ - // --- $test start --- - // type: #type + + static final Object $GOLD = $test(); + + @Test + public static Object $test() { """, - Library.CLASS_HOOK.set( - """ - - static final Object $GOLD = $test(); - - @Test - public static Object $test() { - """, - Library.METHOD_HOOK.set( - template1Body.withArgs(type) - ), - """ - } - - @Check(test = "$test") - public static void $check(Object result) { - Verify.checkEQ(result, $GOLD); - } - - // --- $test end --- - """ - ) + Library.METHOD_HOOK.set( + template1Body.withArgs(type) + ), + """ + } + + @Check(test = "$test") + public static void $check(Object result) { + Verify.checkEQ(result, $GOLD); + } + + // --- $test end --- + """ + ) )); // Now use the templates and add them into the IRTestClass. From 5978068845683237f0230552353b2a0fb04f5956 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 24 Mar 2025 13:57:06 +0100 Subject: [PATCH 205/212] wip predicate refactor --- .../lib/template_library/Dispatcher.java | 28 +++++++++-- .../examples/TestFuzzCode.java | 47 +++++++++---------- 2 files changed, 46 insertions(+), 29 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java b/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java index f08fa857577b4..48dfc7c028d4d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java @@ -24,6 +24,7 @@ package compiler.lib.template_library; import java.util.List; +import java.util.ArrayList; // TODO: rm? import compiler.lib.template_framework.Hook; @@ -51,20 +52,37 @@ public class Dispatcher { //// public static List> basicStatements() { //// } - private final List> templates; + public interface Predicate { + boolean check(); + } + + private static record Element(Template.OneArgs template, Predicate predicate) {} + + private final List elements; + + public Dispatcher() { + this.elements = new ArrayList<>(); + } + + public void add(Template.OneArgs template, Predicate predicate) { + elements.add(new Element(template, predicate)); + } + + public void add(Template.OneArgs template) { + elements.add(new Element(template, () -> { return true; })); + } - public Dispatcher(List> templates) { - this.templates = templates; + private TemplateWithArgs chooseTemplate() { + return Library.choice(elements).template.withArgs(this); } public TemplateWithArgs call() { - // TODO: something! var template = Template.make(() -> body( setFuelCost(0), let("fuel", fuel()), "// $dispatch fuel: #fuel\n", (fuel() <= 0) ? "// $empty\n" - : Library.choice(templates).withArgs(this) + : chooseTemplate() )); return template.withArgs(); } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java index a193e4511ba3c..5c68fff06de43 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java @@ -89,30 +89,29 @@ public static String generate(CompileFramework comp) { List.of()); - Dispatcher dispatcher = new Dispatcher(List.of( - Template.make("dispatcher", (Dispatcher d) -> body( - """ - { // x $open - """, - d.call(), - """ - } // x $close - """ - )), - Template.make("dispatcher", (Dispatcher d) -> body( - """ - { // y $open - """, - d.call(), - """ - // y $mid - """, - d.call(), - """ - } // y $close - """ - )) - )); + Dispatcher dispatcher = new Dispatcher(); + dispatcher.add(Template.make("dispatcher", (Dispatcher d) -> body( + """ + { // x $open + """, + d.call(), + """ + } // x $close + """ + ))); + dispatcher.add(Template.make("dispatcher", (Dispatcher d) -> body( + """ + { // y $open + """, + d.call(), + """ + // y $mid + """, + d.call(), + """ + } // y $close + """ + ))); var template1Body = Template.make("type", (Type type)-> body( setFuelCost(0), From 5f029374c2f9ae3d2b460f392ba4afe103f24316 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 24 Mar 2025 15:15:01 +0100 Subject: [PATCH 206/212] weights in dispatcher --- .../lib/template_library/Dispatcher.java | 35 ++++++++++++++----- .../examples/TestFuzzCode.java | 23 ++++++++++++ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java b/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java index 48dfc7c028d4d..c5f4dbe74e440 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java @@ -25,6 +25,13 @@ import java.util.List; import java.util.ArrayList; +import java.util.NavigableMap; +import java.util.Random; +import java.util.TreeMap; +import java.util.Map; + +import java.util.Random; +import jdk.test.lib.Utils; // TODO: rm? import compiler.lib.template_framework.Hook; @@ -41,6 +48,8 @@ * TODO */ public class Dispatcher { + private static final Random RANDOM = Utils.getRandomInstance(); + //// // Ok, now we want to add random code. We want some kind of dispatcher, that we return to //// // all the time. Can we also have conditions for Templates, to make sure we only choose //// // them when their condition is present? @@ -58,22 +67,32 @@ public interface Predicate { private static record Element(Template.OneArgs template, Predicate predicate) {} - private final List elements; + private final NavigableMap elements = new TreeMap<>(); + private long totalWeight = 0; - public Dispatcher() { - this.elements = new ArrayList<>(); - } + public Dispatcher() {} - public void add(Template.OneArgs template, Predicate predicate) { - elements.add(new Element(template, predicate)); + public void add(Template.OneArgs template, Predicate predicate, long weight) { + totalWeight += weight; + elements.put(totalWeight - 1, new Element(template, predicate)); } public void add(Template.OneArgs template) { - elements.add(new Element(template, () -> { return true; })); + add(template, () -> { return true; }, 1); } private TemplateWithArgs chooseTemplate() { - return Library.choice(elements).template.withArgs(this); + long r = RANDOM.nextLong(totalWeight); + Map.Entry entry = elements.ceilingEntry(r); + long first = entry.getKey(); + while(!entry.getValue().predicate.check()) { + // Step forward, possibly wrapping. + long k = entry.getKey() + 1; + k = (k < totalWeight) ? k : 0; + entry = elements.ceilingEntry(k); + if (entry.getKey() == first) { throw new RuntimeException("none passed predicate"); } + } + return entry.getValue().template.withArgs(this); } public TemplateWithArgs call() { diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java index 5c68fff06de43..1194792a0b337 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java @@ -49,12 +49,15 @@ import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; import static compiler.lib.template_framework.Template.addName; +import static compiler.lib.template_framework.Template.sampleName; +import static compiler.lib.template_framework.Template.weighNames; import static compiler.lib.template_framework.Template.setFuelCost; import compiler.lib.template_library.Library; import compiler.lib.template_library.Dispatcher; import compiler.lib.template_library.IRTestClass; import compiler.lib.template_library.Type; +import compiler.lib.template_library.Expression; /** * TODO @@ -112,6 +115,26 @@ public static String generate(CompileFramework comp) { } // y $close """ ))); + for (Type type : Type.PRIMITIVE_TYPES) { + dispatcher.add(Template.make("dispatcher", (Dispatcher d) -> { + Expression expression = Expression.make(type, Type.PRIMITIVE_TYPES, 2); + return body( + let("type", type), + let("name", sampleName(type, true).name()), + """ + { // z $open type: #type + """, + "#name = ", expression.withRandomArgs(), ";\n", + """ + // z $mid + """, + d.call(), + """ + } // z $close + """ + ); + }), () -> weighNames(type, true) > 0, 1); + } var template1Body = Template.make("type", (Type type)-> body( setFuelCost(0), From 19775a8732a7360b65429816a6b824243f73f421 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 24 Mar 2025 15:33:53 +0100 Subject: [PATCH 207/212] wip fuzz code --- .../lib/template_library/Dispatcher.java | 2 +- .../examples/TestFuzzCode.java | 55 +++++++++---------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java b/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java index c5f4dbe74e440..16397fd5ac448 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java @@ -78,7 +78,7 @@ public void add(Template.OneArgs template, Predicate predicate, long } public void add(Template.OneArgs template) { - add(template, () -> { return true; }, 1); + add(template, () -> true, 100); } private TemplateWithArgs chooseTemplate() { diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java index 1194792a0b337..1abaac0e364ab 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java @@ -94,46 +94,37 @@ public static String generate(CompileFramework comp) { Dispatcher dispatcher = new Dispatcher(); dispatcher.add(Template.make("dispatcher", (Dispatcher d) -> body( - """ - { // x $open - """, - d.call(), - """ - } // x $close - """ + setFuelCost(5), + d.call() ))); dispatcher.add(Template.make("dispatcher", (Dispatcher d) -> body( - """ - { // y $open - """, - d.call(), - """ - // y $mid - """, + setFuelCost(5), d.call(), - """ - } // y $close - """ + d.call() ))); for (Type type : Type.PRIMITIVE_TYPES) { + // Write to mutable variable. dispatcher.add(Template.make("dispatcher", (Dispatcher d) -> { Expression expression = Expression.make(type, Type.PRIMITIVE_TYPES, 2); return body( + setFuelCost(1), let("type", type), let("name", sampleName(type, true).name()), - """ - { // z $open type: #type - """, "#name = ", expression.withRandomArgs(), ";\n", - """ - // z $mid - """, - d.call(), - """ - } // z $close - """ + d.call() ); - }), () -> weighNames(type, true) > 0, 1); + }), () -> weighNames(type, true) > 0, 100); + // New mutable variable. + dispatcher.add(Template.make("dispatcher", (Dispatcher d) -> { + Expression expression = Expression.make(type, Type.PRIMITIVE_TYPES, 2); + return body( + setFuelCost(1), + let("type", type), + addName(new Name($("var"), type, true, 1)), + "#type $var = ", expression.withRandomArgs(), ";\n", + d.call() + ); + }), () -> true, 100); } var template1Body = Template.make("type", (Type type)-> body( @@ -142,8 +133,14 @@ public static String generate(CompileFramework comp) { // by the random code. addName(new Name($("ret"), type, true, 10)), "#type $ret = ", type.con(), ";\n", + "try {\n", dispatcher.call(), - "return $ret;\n" + """ + } catch (Exception e) { + return new Object[] {e, $ret }; + } + return $ret; + """ )); var template1 = Template.make("type", (Type type) -> body( setFuelCost(0), From c943e1337eeafd428750cf0ef4dd1ffd44080198 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 24 Mar 2025 16:05:34 +0100 Subject: [PATCH 208/212] wip, incl if statement --- .../lib/template_library/Dispatcher.java | 2 -- .../examples/TestFuzzCode.java | 20 +++++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java b/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java index 16397fd5ac448..08d695fb50785 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Dispatcher.java @@ -98,8 +98,6 @@ private TemplateWithArgs chooseTemplate() { public TemplateWithArgs call() { var template = Template.make(() -> body( setFuelCost(0), - let("fuel", fuel()), - "// $dispatch fuel: #fuel\n", (fuel() <= 0) ? "// $empty\n" : chooseTemplate() )); diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java index 1abaac0e364ab..9b9979cf1befa 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java @@ -93,15 +93,24 @@ public static String generate(CompileFramework comp) { Dispatcher dispatcher = new Dispatcher(); + + // Split. dispatcher.add(Template.make("dispatcher", (Dispatcher d) -> body( - setFuelCost(5), + setFuelCost(10), + d.call(), d.call() ))); + + // If statement. dispatcher.add(Template.make("dispatcher", (Dispatcher d) -> body( - setFuelCost(5), + setFuelCost(10), + "if (", Expression.make(Type.booleans(), Type.PRIMITIVE_TYPES, 2).withRandomArgs(), ") {\n", d.call(), - d.call() + "} else {\n", + d.call(), + "}\n" ))); + for (Type type : Type.PRIMITIVE_TYPES) { // Write to mutable variable. dispatcher.add(Template.make("dispatcher", (Dispatcher d) -> { @@ -114,6 +123,7 @@ public static String generate(CompileFramework comp) { d.call() ); }), () -> weighNames(type, true) > 0, 100); + // New mutable variable. dispatcher.add(Template.make("dispatcher", (Dispatcher d) -> { Expression expression = Expression.make(type, Type.PRIMITIVE_TYPES, 2); @@ -124,7 +134,7 @@ public static String generate(CompileFramework comp) { "#type $var = ", expression.withRandomArgs(), ";\n", d.call() ); - }), () -> true, 100); + })); } var template1Body = Template.make("type", (Type type)-> body( @@ -137,6 +147,8 @@ public static String generate(CompileFramework comp) { dispatcher.call(), """ } catch (Exception e) { + // Exceptions can be thrown by the expression. We capture it, + // together with the current state of the $ret variable. return new Object[] {e, $ret }; } return $ret; From 921d82ef9bfd05fb9e1601a22fa0e55b6bf50fda Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 24 Mar 2025 16:53:02 +0100 Subject: [PATCH 209/212] basic for loop example --- .../lib/template_library/Operations.java | 2 ++ .../template_library/examples/TestFuzzCode.java | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java index fa76be90450a1..67630e97e0ad2 100644 --- a/test/hotspot/jtreg/compiler/lib/template_library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_library/Operations.java @@ -292,6 +292,8 @@ public final class Operations { private static final List BOOLEAN_OPERATIONS = List.of( // Note: there is no casting / conversion from an to boolean directly. + // TODO: add comparison operators: <, >, ... + new Operation.Unary(Type.booleans(), "(!(", Type.booleans(), "))", null), new Operation.Binary(Type.booleans(), "(", Type.booleans(), " || ", Type.booleans(), ")", null), diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java index 9b9979cf1befa..b82485ae90e3d 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java @@ -108,7 +108,21 @@ public static String generate(CompileFramework comp) { d.call(), "} else {\n", d.call(), - "}\n" + "}\n", + d.call() + ))); + + // Simple counted loop. + dispatcher.add(Template.make("dispatcher", (Dispatcher d) -> body( + setFuelCost(20), + addName(new Name($("i"), Type.ints(), false, 1)), + """ + for (int $i = 0; $i < 100; $i++) { + """, + d.call(), + """ + } + """ ))); for (Type type : Type.PRIMITIVE_TYPES) { From cc4a96ebea71f8a58ca7b8259f4cfbad1126e4c0 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 24 Mar 2025 17:04:42 +0100 Subject: [PATCH 210/212] limit variables --- .../template_library/examples/TestFuzzCode.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java index b82485ae90e3d..8954505b1a452 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzCode.java @@ -126,7 +126,7 @@ public static String generate(CompileFramework comp) { ))); for (Type type : Type.PRIMITIVE_TYPES) { - // Write to mutable variable. + // Write to existing mutable variable. dispatcher.add(Template.make("dispatcher", (Dispatcher d) -> { Expression expression = Expression.make(type, Type.PRIMITIVE_TYPES, 2); return body( @@ -148,7 +148,8 @@ public static String generate(CompileFramework comp) { "#type $var = ", expression.withRandomArgs(), ";\n", d.call() ); - })); + // Limit adding more variables, otherwise we can use existing ones. + }), () -> weighNames(type, true) < 20, 100); } var template1Body = Template.make("type", (Type type)-> body( From 89a5600b51cbc1301f7961689be1304c80747c53 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 25 Mar 2025 09:09:07 +0100 Subject: [PATCH 211/212] revert spurious changes --- src/hotspot/share/opto/vectornode.cpp | 3 +-- test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index e561fe117defa..0e5739f39c2ed 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -1492,8 +1492,7 @@ VectorNode* VectorCastNode::make(int vopc, Node* n1, BasicType bt, uint vlen) { } int VectorCastNode::opcode(int sopc, BasicType bt, bool is_signed) { - assert((is_integral_type(bt) && bt != T_LONG) || is_signed, "sopc=%s, bt=%s, is_signed=%s", - NodeClassNames[sopc], type2name(bt), is_signed ? "true" : "false"); + assert((is_integral_type(bt) && bt != T_LONG) || is_signed, ""); // Handle special case for to/from Half Float conversions switch (sopc) { diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java b/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java index f42ba47af226e..7fde22cd20723 100644 --- a/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java @@ -39,7 +39,7 @@ * Helper class for compilation of Java and Jasm {@link SourceCode}. */ class Compile { - private static final int COMPILE_TIMEOUT = 600; // TODO: only temporarily increasing for windows failures. + private static final int COMPILE_TIMEOUT = 60; private static final String JAVA_PATH = JDKToolFinder.getJDKTool("java"); private static final String JAVAC_PATH = JDKToolFinder.getJDKTool("javac"); From ec3915caa9136165b9372fe18c73ccc39ce37987 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 25 Mar 2025 16:58:41 +0100 Subject: [PATCH 212/212] catch exceptions more properly --- .../template_library/examples/TestFuzzExpression.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java index 749747d96de15..5aa8eef1c23c0 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_library/examples/TestFuzzExpression.java @@ -132,9 +132,12 @@ public static String generate(CompileFramework comp) { try { """, " return ", expression.withArgs(use), ";\n", + expression.exceptions().stream().map(exception -> + "} catch (" + exception + " e) { return e;\n" + ).toList(), """ - } catch (Exception e) { - return e; + } finally { + // Just javac is happy if there are no exceptions to catch. } }