From 2d719dcee8a5b42a99c167de201acef4b28f69f6 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Mon, 4 May 2026 09:00:21 +0000 Subject: [PATCH 1/3] 8381809: Template Framework Library: add Float16Vector type --- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 19 ++++- .../library/CodeGenerationDataNameType.java | 17 +++++ .../library/Operations.java | 70 +++++++++++-------- .../library/PrimitiveType.java | 59 ++++++++++++++-- .../library/VectorType.java | 9 ++- .../vectorapi/VectorExpressionFuzzer.java | 20 +++--- 6 files changed, 149 insertions(+), 45 deletions(-) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index dfb2ec1405c1b..6734f9dd9b006 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -335,13 +335,26 @@ public static String generate(CompileFramework comp) { ); }); + // Filter out expressions involving the float16 PrimitiveType, which is a vector-lane + // carrier (short-backed) and not a valid Java scalar type keyword. + java.util.function.Predicate noFloat16 = e -> + !e.returnType.name().equals("float16") && + e.argumentTypes.stream().noneMatch(t -> t.name().equals("float16")); + + List primitiveOps = Operations.PRIMITIVE_OPERATIONS.stream() + .filter(noFloat16).toList(); + List scalarNumericOps = Operations.SCALAR_NUMERIC_OPERATIONS.stream() + .filter(noFloat16).toList(); + // Generate expressions with the primitive types. for (PrimitiveType type : PRIMITIVE_TYPES) { - // Prmitive expressions are most important, so let's create many expressions per output type. + // Skip the float16 PrimitiveType: it is a vector-lane carrier, not a true scalar. + if (type.name().equals("float16")) continue; + // Primitive expressions are most important, so let's create many expressions per output type. for (int i = 0; i < 10; i++) { // The depth determines roughly how many operations are going to be used in the expression. int depth = RANDOM.nextInt(1, 20); - Expression expression = Expression.nestRandomly(type, Operations.PRIMITIVE_OPERATIONS, depth, Nesting.EXACT); + Expression expression = Expression.nestRandomly(type, primitiveOps, depth, Nesting.EXACT); tests.add(testTemplate.asToken(expression)); } } @@ -356,7 +369,7 @@ public static String generate(CompileFramework comp) { for (int i = 0; i < 2; i++) { // The depth determines roughly how many operations are going to be used in the expression. int depth = RANDOM.nextInt(1, 20); - Expression expression = Expression.nestRandomly(type, Operations.SCALAR_NUMERIC_OPERATIONS, depth, Nesting.EXACT); + Expression expression = Expression.nestRandomly(type, scalarNumericOps, depth, Nesting.EXACT); tests.add(testTemplate.asToken(expression)); } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java index 33eba66cd8c9a..a0389a27d468c 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java @@ -58,6 +58,13 @@ public interface CodeGenerationDataNameType extends DataName.Type { */ static PrimitiveType shorts() { return PrimitiveType.SHORTS; } + /** + * The short {@link PrimitiveType}. + * + * @return The short {@link PrimitiveType}. + */ + static PrimitiveType float16s() { return PrimitiveType.FLOAT16S; } + /** * The char {@link PrimitiveType}. * @@ -114,6 +121,7 @@ public interface CodeGenerationDataNameType extends DataName.Type { bytes(), chars(), shorts(), + float16s(), ints(), longs(), floats(), @@ -153,6 +161,7 @@ public interface CodeGenerationDataNameType extends DataName.Type { * List of all floating {@link PrimitiveType}s (float, double). */ List FLOATING_TYPES = List.of( + float16s(), floats(), doubles() ); @@ -199,6 +208,13 @@ public interface CodeGenerationDataNameType extends DataName.Type { VectorType.SHORT_512 ); + List VECTOR_FLOAT16_VECTOR_TYPES = List.of( + VectorType.FLOAT16_64, + VectorType.FLOAT16_128, + VectorType.FLOAT16_256, + VectorType.FLOAT16_512 + ); + List VECTOR_INT_VECTOR_TYPES = List.of( VectorType.INT_64, VectorType.INT_128, @@ -230,6 +246,7 @@ public interface CodeGenerationDataNameType extends DataName.Type { List VECTOR_VECTOR_TYPES = Utils.concat( VECTOR_BYTE_VECTOR_TYPES, VECTOR_SHORT_VECTOR_TYPES, + VECTOR_FLOAT16_VECTOR_TYPES, VECTOR_INT_VECTOR_TYPES, VECTOR_LONG_VECTOR_TYPES, VECTOR_FLOAT_VECTOR_TYPES, diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index ecd6f42de8df6..3794a3a43ed56 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -33,6 +33,7 @@ import static compiler.lib.template_framework.library.PrimitiveType.INTS; import static compiler.lib.template_framework.library.PrimitiveType.LONGS; import static compiler.lib.template_framework.library.PrimitiveType.FLOATS; +import static compiler.lib.template_framework.library.PrimitiveType.FLOAT16S; import static compiler.lib.template_framework.library.PrimitiveType.DOUBLES; import static compiler.lib.template_framework.library.PrimitiveType.BOOLEANS; import static compiler.lib.template_framework.library.Float16Type.FLOAT16; @@ -106,11 +107,17 @@ private static List generatePrimitiveOperations() { }); CodeGenerationDataNameType.FLOATING_TYPES.stream().forEach(type -> { + // float16 uses short as its carrier type; Java promotes short arithmetic + // results to int, so all arithmetic expressions need an explicit (short) cast. + boolean needsCast = (type == FLOAT16S); + String cb = needsCast ? "((short)(" : "("; + String ce = needsCast ? "))" : ")"; + // Arithmetic operators - ops.add(Expression.make(type, "(-(", type, "))")); - ops.add(Expression.make(type, "(", type, " + ", type, ")")); - ops.add(Expression.make(type, "(", type, " - ", type, ")")); - ops.add(Expression.make(type, "(", type, " * ", type, ")")); + ops.add(Expression.make(type, cb + "-(", type, ")" + ce)); + ops.add(Expression.make(type, cb, type, " + ", type, ce)); + ops.add(Expression.make(type, cb, type, " - ", type, ce)); + ops.add(Expression.make(type, cb, type, " * ", type, ce)); // Because of subtyping, we can sample an expression like `(float)((int)(3) / (int)(0))`. Floating point // division and modulo do not throw an ArithmeticException on division by zero, integer division and modulo // do. In the expression above, the division has an integer on both sides, so it is executed as an integer @@ -118,10 +125,14 @@ private static List generatePrimitiveOperations() { // To prevent this issue, we provide two versions of floating point division operations: one that casts // its operands and one that expects that an ArithmeticException might be thrown when we get unlucky when // sampling subtypes. - ops.add(Expression.make(type, "((" + type.name() + ")(", type, ") / (" + type.name() +")(", type, "))")); - ops.add(Expression.make(type, "((" + type.name() + ")(", type, ") % (" + type.name() +")(", type, "))")); - ops.add(Expression.make(type, "(", type, " / ", type, ")", WITH_ARITHMETIC_EXCEPTION)); - ops.add(Expression.make(type, "(", type, " % ", type, ")", WITH_ARITHMETIC_EXCEPTION)); + // Per-operand casts don't help float16 since its carrier type (short) always + // uses integer division regardless of casts. + if (!needsCast) { + ops.add(Expression.make(type, "((" + type.name() + ")(", type, ") / (" + type.name() +")(", type, "))")); + ops.add(Expression.make(type, "((" + type.name() + ")(", type, ") % (" + type.name() +")(", type, "))")); + } + ops.add(Expression.make(type, cb, type, " / ", type, ce, WITH_ARITHMETIC_EXCEPTION)); + ops.add(Expression.make(type, cb, type, " % ", type, ce, WITH_ARITHMETIC_EXCEPTION)); // Relational / Comparison Operators ops.add(Expression.make(BOOLEANS, "(", type, " == ", type, ")")); @@ -472,14 +483,14 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofCast(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), 0))")); + + type2.elementType.className() + ".class, " + + type.elementType.className() + ".class), 0))")); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofCast(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class),", + + type2.elementType.className() + ".class, " + + type.elementType.className() + ".class),", INTS, // part "))", WITH_OUT_OF_BOUNDS_EXCEPTION)); } @@ -494,14 +505,14 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), 0))", reinterpretInfo)); + + type2.elementType.className() + ".class, " + + type.elementType.className() + ".class), 0))", reinterpretInfo)); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class),", + + type2.elementType.className() + ".class, " + + type.elementType.className() + ".class),", INTS, // part "))", reinterpretInfo.combineWith(WITH_OUT_OF_BOUNDS_EXCEPTION))); if (type.elementType == BYTES) { @@ -519,6 +530,9 @@ private static List generateVectorOperations() { if (type.elementType == FLOATS) { ops.add(Expression.make(type, "", type2, ".reinterpretAsFloats()", reinterpretInfo)); } + if (type.elementType == FLOAT16S) { + ops.add(Expression.make(type, "", type2, ".reinterpretAsFloat16s()", reinterpretInfo)); + } if (type.elementType == DOUBLES) { ops.add(Expression.make(type, "", type2, ".reinterpretAsDoubles()", reinterpretInfo)); } @@ -554,8 +568,8 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofCast(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.className() + ".class, " + + type.elementType.className() + ".class), " + type.speciesName + ", ", INTS, // part "))", WITH_OUT_OF_BOUNDS_EXCEPTION)); @@ -563,8 +577,8 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.className() + ".class, " + + type.elementType.className() + ".class), " + type.speciesName + ", ", INTS, // part "))", reinterpretInfo.combineWith(WITH_OUT_OF_BOUNDS_EXCEPTION))); @@ -581,16 +595,16 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofCast(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.className() + ".class, " + + type.elementType.className() + ".class), " + type.speciesName + ", ", INTS, " & " + partMask + "))")); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.className() + ".class, " + + type.elementType.className() + ".class), " + type.speciesName + ", ", INTS, " & " + partMask + "))", reinterpretInfo)); } else { @@ -600,16 +614,16 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofCast(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.className() + ".class, " + + type.elementType.className() + ".class), " + type.speciesName + ", " + "-(", INTS, " & " + partMask + ")))")); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.className() + ".class, " + + type.elementType.className() + ".class), " + type.speciesName + ", " + "-(", INTS, " & " + partMask + ")))", reinterpretInfo)); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java index cd796fd0d3144..377809ea39d2d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java @@ -50,8 +50,9 @@ public final class PrimitiveType implements CodeGenerationDataNameType { 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(); + private static final Generator GEN_FLOAT16 = Generators.G.float16s(); - private static enum Kind { BYTE, SHORT, CHAR, INT, LONG, FLOAT, DOUBLE, BOOLEAN }; + private static enum Kind { BYTE, SHORT, CHAR, INT, LONG, FLOAT, DOUBLE, BOOLEAN, FLOAT16 }; // We have one static instance each, so we do not have duplicated instances. static final PrimitiveType BYTES = new PrimitiveType(Kind.BYTE ); @@ -62,6 +63,7 @@ private static enum Kind { BYTE, SHORT, CHAR, INT, LONG, FLOAT, DOUBLE, BOOLEAN static final PrimitiveType FLOATS = new PrimitiveType(Kind.FLOAT ); static final PrimitiveType DOUBLES = new PrimitiveType(Kind.DOUBLE ); static final PrimitiveType BOOLEANS = new PrimitiveType(Kind.BOOLEAN); + static final PrimitiveType FLOAT16S = new PrimitiveType(Kind.FLOAT16); final Kind kind; @@ -104,6 +106,38 @@ public String name() { case FLOAT -> "float"; case DOUBLE -> "double"; case BOOLEAN -> "boolean"; + case FLOAT16 -> "float16"; + }; + } + + /** + * Returns the Vector lane carrier type name. For most types this is the + * same as {@link #name()}, but for {@code float16} the carrier type is + * {@code short}. + */ + public String cname() { + return switch (kind) { + case BYTE -> "byte"; + case SHORT -> "short"; + case CHAR -> "char"; + case INT -> "int"; + case LONG -> "long"; + case FLOAT -> "float"; + case DOUBLE -> "double"; + case BOOLEAN -> "boolean"; + case FLOAT16 -> "short"; + }; + } + + /** + * Returns the name used in {@code .class} literals. For Java primitives + * this is the primitive keyword (e.g. {@code int}). For {@code float16} + * it is {@code Float16} since there is no {@code float16} keyword. + */ + public String className() { + return switch (kind) { + case FLOAT16 -> "Float16"; + default -> name(); }; } @@ -118,6 +152,7 @@ public Object con() { case BYTE -> "(byte)" + GEN_BYTE.next(); case SHORT -> "(short)" + GEN_SHORT.next(); case CHAR -> "(char)" + GEN_CHAR.next(); + case FLOAT16 -> "(short)" + GEN_FLOAT16.next(); case INT -> GEN_INT.next(); case LONG -> GEN_LONG.next(); case FLOAT -> GEN_FLOAT.next(); @@ -135,7 +170,7 @@ public Object con() { public int byteSize() { return switch (kind) { case BYTE -> 1; - case SHORT, CHAR -> 2; + case SHORT, CHAR, FLOAT16 -> 2; case INT, FLOAT -> 4; case LONG, DOUBLE -> 8; case BOOLEAN -> { throw new UnsupportedOperationException("boolean does not have a defined 'size'"); } @@ -157,6 +192,7 @@ public String boxedTypeName() { case FLOAT -> "Float"; case DOUBLE -> "Double"; case BOOLEAN -> "Boolean"; + case FLOAT16 -> "Float16"; }; } @@ -169,6 +205,7 @@ public String fieldDesc() { return switch (kind) { case LONG -> "J"; case BOOLEAN -> "Z"; + case FLOAT16 -> "S"; default -> boxedTypeName().substring(0, 1); }; } @@ -197,7 +234,7 @@ public String abbrev() { public boolean isFloating() { return switch (kind) { case BYTE, SHORT, CHAR, INT, LONG, BOOLEAN -> false; - case FLOAT, DOUBLE -> true; + case FLOAT16, FLOAT, DOUBLE -> true; }; } @@ -223,6 +260,7 @@ public Object callLibraryRNG() { case FLOAT -> "LibraryRNG.nextFloat()"; case DOUBLE -> "LibraryRNG.nextDouble()"; case BOOLEAN -> "LibraryRNG.nextBoolean()"; + case FLOAT16 -> "LibraryRNG.nextFloat16()"; }; } @@ -250,6 +288,7 @@ public static class LibraryRNG { 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(); + private static final Generator GEN_FLOAT16 = Generators.G.float16s(); public static byte nextByte() { return GEN_BYTE.next().byteValue(); @@ -283,8 +322,20 @@ public static boolean nextBoolean() { return RANDOM.nextBoolean(); } + public static short nextFloat16() { + return GEN_FLOAT16.next(); + } + + public static void fill_float16(short[] a) { + for (int i = 0; i < a.length; i++) { + a[i] = nextFloat16(); + } + } + """, - CodeGenerationDataNameType.PRIMITIVE_TYPES.stream().map(type -> scope( + CodeGenerationDataNameType.PRIMITIVE_TYPES.stream() + .filter(type -> !type.cname().equals("short") || type.name().equals("short")) + .map(type -> scope( let("type", type), """ public static void fill(#type[] a) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java index 7eabd42a723f0..ff09889f7591c 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java @@ -36,6 +36,7 @@ import static compiler.lib.template_framework.library.PrimitiveType.FLOATS; import static compiler.lib.template_framework.library.PrimitiveType.DOUBLES; import static compiler.lib.template_framework.library.PrimitiveType.BOOLEANS; +import static compiler.lib.template_framework.library.PrimitiveType.FLOAT16S; /** * The {@link VectorType} models the Vector API types. @@ -73,6 +74,11 @@ public abstract class VectorType implements CodeGenerationDataNameType { public static final VectorType.Vector DOUBLE_256 = new VectorType.Vector(DOUBLES, 4); public static final VectorType.Vector DOUBLE_512 = new VectorType.Vector(DOUBLES, 8); + public static final VectorType.Vector FLOAT16_64 = new VectorType.Vector(FLOAT16S, 4); + public static final VectorType.Vector FLOAT16_128 = new VectorType.Vector(FLOAT16S, 8); + public static final VectorType.Vector FLOAT16_256 = new VectorType.Vector(FLOAT16S, 16); + public static final VectorType.Vector FLOAT16_512 = new VectorType.Vector(FLOAT16S, 32); + private final String vectorTypeName; private VectorType(String vectorTypeName) { @@ -104,6 +110,7 @@ private static final String vectorTypeName(PrimitiveType elementType) { case "long" -> "LongVector"; case "float" -> "FloatVector"; case "double" -> "DoubleVector"; + case "float16" -> "Float16Vector"; default -> throw new UnsupportedOperationException("Not supported: " + elementType.name()); }; } @@ -132,7 +139,7 @@ public final Object con() { return List.of(name(), ".zero(", speciesName, ")"); } else if (r <= 8) { return List.of( - name(), ".fromArray(", speciesName, ", new ", elementType.name(), "[] {", + name(), ".fromArray(", speciesName, ", new ", elementType.cname(), "[] {", elementType.con(), Stream.generate(() -> List.of(", ", elementType.con()) diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java b/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java index ea36cca4b929a..e536a6a99e1a1 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java @@ -31,7 +31,7 @@ * @modules java.base/jdk.internal.misc * @library /test/lib / * @compile ../../compiler/lib/verify/Verify.java - * @run driver ${test.main.class} -XX:UseAVX=2 + * @run driver ${test.main.class} -XX:UseAVX=3 */ // TODO: remove the x64 and linux restriction above. I added that for now so we are not flooded @@ -162,7 +162,7 @@ public static String generate(CompileFramework comp) { // - Input values are delivered via fields or array loads. // - The final vector is written into an array, and that array is returned. var template2Body = Template.make("expression", "arguments", (Expression expression, List arguments) -> scope( - let("elementType", ((VectorType.Vector)expression.returnType).elementType), + let("elementType", ((VectorType.Vector)expression.returnType).elementType.cname()), """ try { #elementType[] out = new #elementType[1000]; @@ -201,10 +201,11 @@ public static String generate(CompileFramework comp) { } case 1 -> { // Create the constant outside, and pass it. + String typeName = (argumentType instanceof PrimitiveType pt) ? pt.cname() : argumentType.name(); arguments.add(new TestArgument( - List.of(argumentType.name(), " ", name, " = ", argumentType.con(), ";\n"), + List.of(typeName, " ", name, " = ", argumentType.con(), ";\n"), name, - List.of(argumentType.name(), " ", name), + List.of(typeName, " ", name), name )); } @@ -215,18 +216,19 @@ public static String generate(CompileFramework comp) { // so we get the same value for both test and reference. If we called LibraryRNG // for "use", we would get separate values, which is not helpful. arguments.add(new TestArgument( - List.of(t.name(), " ", name, " = ", t.callLibraryRNG(), ";\n"), + List.of(t.cname(), " ", name, " = ", t.callLibraryRNG(), ";\n"), name, - List.of(t.name(), " ", name), + List.of(t.cname(), " ", name), name )); } else if (argumentType instanceof VectorType.Vector t) { PrimitiveType et = t.elementType; + String fillMethod = et.name().equals("float16") ? "fill_float16" : "fill"; arguments.add(new TestArgument( - List.of(et.name(), "[] ", name, " = new ", et.name(), "[1000];\n", - "LibraryRNG.fill(", name,");\n"), + List.of(et.cname(), "[] ", name, " = new ", et.cname(), "[1000];\n", + "LibraryRNG.", fillMethod, "(", name,");\n"), name, - List.of(et.name(), "[] ", name), + List.of(et.cname(), "[] ", name), List.of(t.name(), ".fromArray(", t.speciesName, ", ", name, ", 0)") )); } else if (argumentType instanceof VectorType.Mask t) { From 47c00eff1a0b50201fac86d2ac5884a3bd215388 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Mon, 1 Jun 2026 12:51:52 +0000 Subject: [PATCH 2/3] Reveiw comments resolutions --- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 19 +- .../library/CodeGenerationDataNameType.java | 113 ++++++++- .../library/Float16VectorType.java | 112 +++++++++ .../library/Operations.java | 228 +++++++++--------- .../library/PrimitiveType.java | 75 +++--- .../library/VectorElementType.java | 96 ++++++++ .../library/VectorType.java | 22 +- .../vectorapi/VectorExpressionFuzzer.java | 74 +++--- 8 files changed, 523 insertions(+), 216 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/library/Float16VectorType.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/library/VectorElementType.java diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index 6734f9dd9b006..dfb2ec1405c1b 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -335,26 +335,13 @@ public static String generate(CompileFramework comp) { ); }); - // Filter out expressions involving the float16 PrimitiveType, which is a vector-lane - // carrier (short-backed) and not a valid Java scalar type keyword. - java.util.function.Predicate noFloat16 = e -> - !e.returnType.name().equals("float16") && - e.argumentTypes.stream().noneMatch(t -> t.name().equals("float16")); - - List primitiveOps = Operations.PRIMITIVE_OPERATIONS.stream() - .filter(noFloat16).toList(); - List scalarNumericOps = Operations.SCALAR_NUMERIC_OPERATIONS.stream() - .filter(noFloat16).toList(); - // Generate expressions with the primitive types. for (PrimitiveType type : PRIMITIVE_TYPES) { - // Skip the float16 PrimitiveType: it is a vector-lane carrier, not a true scalar. - if (type.name().equals("float16")) continue; - // Primitive expressions are most important, so let's create many expressions per output type. + // Prmitive expressions are most important, so let's create many expressions per output type. for (int i = 0; i < 10; i++) { // The depth determines roughly how many operations are going to be used in the expression. int depth = RANDOM.nextInt(1, 20); - Expression expression = Expression.nestRandomly(type, primitiveOps, depth, Nesting.EXACT); + Expression expression = Expression.nestRandomly(type, Operations.PRIMITIVE_OPERATIONS, depth, Nesting.EXACT); tests.add(testTemplate.asToken(expression)); } } @@ -369,7 +356,7 @@ public static String generate(CompileFramework comp) { for (int i = 0; i < 2; i++) { // The depth determines roughly how many operations are going to be used in the expression. int depth = RANDOM.nextInt(1, 20); - Expression expression = Expression.nestRandomly(type, scalarNumericOps, depth, Nesting.EXACT); + Expression expression = Expression.nestRandomly(type, Operations.SCALAR_NUMERIC_OPERATIONS, depth, Nesting.EXACT); tests.add(testTemplate.asToken(expression)); } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java index a0389a27d468c..2ad4b235f388d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java @@ -33,6 +33,27 @@ * additional functionality for code generation. These types with their extended * functionality can be used with many other code generation facilities in the * library, such as generating random {@code Expression}s. + * + *

This module distinguishes scalar Java types and + * Vector API lane-element types: + *

    + *
  • Scalar {@code PRIMITIVE_TYPES}/{@code FLOATING_TYPES}/etc. enumerate + * only Java primitive types ({@code byte}, {@code short}, ...). + * These lists are typed as {@code List} and are consumed + * by scalar fuzzers / scalar code generation. {@link Float16Type} (the + * scalar {@code Float16} logical type) is included in + * {@link #SCALAR_NUMERIC_TYPES} only.
  • + *
  • Vector-lane lists ({@code VECTOR_ELEMENT_TYPES}, + * {@code FLOATING_VECTOR_ELEMENT_TYPES}, ...) enumerate the lane types + * valid for {@code VectorType.Vector}. These are typed as + * {@code List} and additionally include + * {@link Float16VectorType#FLOAT16} since {@code Float16Vector} is a real + * Vector API type whose lanes happen to have no Java primitive + * keyword.
  • + *
+ * Vector generators (e.g. {@code Operations.VECTOR_OPERATIONS}) consume the + * vector-lane lists; scalar generators (e.g. + * {@code Operations.PRIMITIVE_OPERATIONS}) consume the scalar lists. */ public interface CodeGenerationDataNameType extends DataName.Type { @@ -58,13 +79,6 @@ public interface CodeGenerationDataNameType extends DataName.Type { */ static PrimitiveType shorts() { return PrimitiveType.SHORTS; } - /** - * The short {@link PrimitiveType}. - * - * @return The short {@link PrimitiveType}. - */ - static PrimitiveType float16s() { return PrimitiveType.FLOAT16S; } - /** * The char {@link PrimitiveType}. * @@ -108,9 +122,19 @@ public interface CodeGenerationDataNameType extends DataName.Type { static PrimitiveType booleans() { return PrimitiveType.BOOLEANS; } /** - * The Float16 type. + * The {@code Float16Vector} lane-element type. This is a + * {@link VectorElementType}, not a Java + * {@link PrimitiveType}; it appears in vector-lane lists but never in the + * scalar {@code PRIMITIVE_TYPES}/{@code FLOATING_TYPES} lists. * - * @return The Float16 type. + * @return The {@code Float16Vector} {@link VectorElementType}. + */ + static Float16VectorType float16s() { return Float16VectorType.FLOAT16; } + + /** + * The {@code Float16} scalar (logical) type. + * + * @return The scalar {@code Float16} type. */ static CodeGenerationDataNameType float16() { return Float16Type.FLOAT16; } @@ -121,7 +145,6 @@ public interface CodeGenerationDataNameType extends DataName.Type { bytes(), chars(), shorts(), - float16s(), ints(), longs(), floats(), @@ -161,7 +184,6 @@ public interface CodeGenerationDataNameType extends DataName.Type { * List of all floating {@link PrimitiveType}s (float, double). */ List FLOATING_TYPES = List.of( - float16s(), floats(), doubles() ); @@ -194,6 +216,75 @@ public interface CodeGenerationDataNameType extends DataName.Type { float16() ); + // -------------------------------------------------------------------- + // Vector API lane-element type lists. + // + // These are typed as List and may include + // Float16VectorType.FLOAT16 in addition to the Java primitive lane + // carriers. They are the source of truth for vector lane iteration in + // vector generators (e.g. Operations.VECTOR_OPS). + // -------------------------------------------------------------------- + + /** + * All Vector API lane-element types: every Java numeric primitive lane + * carrier plus {@link Float16VectorType#FLOAT16}. + */ + List VECTOR_ELEMENT_TYPES = List.of( + bytes(), + shorts(), + float16s(), + ints(), + longs(), + floats(), + doubles() + ); + + /** + * Integral Vector API lane-element types (byte, short, int, long). + */ + List INTEGRAL_VECTOR_ELEMENT_TYPES = List.of( + bytes(), + shorts(), + ints(), + longs() + ); + + /** + * Floating Vector API lane-element types (float16, float, double). + */ + List FLOATING_VECTOR_ELEMENT_TYPES = List.of( + float16s(), + floats(), + doubles() + ); + + /** + * Integral-and-floating Vector API lane-element types: every lane type + * except booleans/chars (which have no Vector API counterparts). + */ + List INTEGRAL_AND_FLOATING_VECTOR_ELEMENT_TYPES = List.of( + bytes(), + shorts(), + float16s(), + ints(), + longs(), + floats(), + doubles() + ); + + /** + * Vector API lane-element types whose lanes are 32/64 bits and integral + * (int, long). + */ + List INT_LONG_VECTOR_ELEMENT_TYPES = List.of( + ints(), + longs() + ); + + // -------------------------------------------------------------------- + // Concrete VectorType lists (typed as the concrete Vector subclasses). + // -------------------------------------------------------------------- + List VECTOR_BYTE_VECTOR_TYPES = List.of( VectorType.BYTE_64, VectorType.BYTE_128, diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Float16VectorType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Float16VectorType.java new file mode 100644 index 0000000000000..9865080a7eff2 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Float16VectorType.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.template_framework.library; + +import compiler.lib.generators.Generators; +import compiler.lib.generators.Generator; + +import compiler.lib.template_framework.DataName; + +/** + * The {@link Float16VectorType} is the {@link VectorElementType} that describes + * the lane type of a {@code Float16Vector}. + * + *

Float16 is not a Java primitive type and therefore does + * not appear in any of the scalar {@link PrimitiveType} lists. As a + * {@link VectorElementType} it appears in vector-lane-typed lists such as + * {@link CodeGenerationDataNameType#VECTOR_ELEMENT_TYPES} and + * {@link CodeGenerationDataNameType#FLOATING_VECTOR_ELEMENT_TYPES}, which are + * consumed by vector-only generators (e.g. {@code Operations.VECTOR_OPERATIONS}). + * + *

The carrier type for a {@code Float16Vector} lane is {@code short}; the + * element type token used in {@code VectorOperators.Conversion.of*} expressions + * and {@code Float16Vector.SPECIES_*} is {@code Float16}. + * + *

NaN handling note: there are multiple bit representations for NaN within + * {@code short}/{@code Float16}. Consumers comparing {@code short[]} carrier + * arrays should canonicalize via {@code Float.float16ToFloat} (which returns a + * canonical NaN) before structural comparison. + */ +public final class Float16VectorType implements VectorElementType { + private static final Generator GEN_FLOAT16 = Generators.G.float16s(); + + /** The singleton instance. */ + public static final Float16VectorType FLOAT16 = new Float16VectorType(); + + private Float16VectorType() {} + + @Override + public boolean isSubtypeOf(DataName.Type other) { + return other instanceof Float16VectorType; + } + + @Override + public String name() { + return "float16"; + } + + @Override + public String carrierTypeName() { + return "short"; + } + + @Override + public String elementTypeName() { + return "Float16"; + } + + @Override + public String boxedTypeName() { + return "Float16"; + } + + @Override + public int byteSize() { + return 2; + } + + @Override + public boolean isFloating() { + return true; + } + + @Override + public String toString() { + // Used by Template `let(...)` hashtag substitution as a Java scalar + // type for a single lane. Float16 has no Java keyword, so we return + // the carrier ("short"), which is what Float16Vector.lane(int) returns + // and what Float16Vector.broadcast(SPECIES, ...) accepts. + return carrierTypeName(); + } + + @Override + public Object con() { + return "(short)" + GEN_FLOAT16.next(); + } + + @Override + public Object callLibraryRNG() { + return "LibraryRNG.nextFloat16()"; + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index 2e23f428ab750..5dd5998b6d464 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -33,7 +33,6 @@ import static compiler.lib.template_framework.library.PrimitiveType.INTS; import static compiler.lib.template_framework.library.PrimitiveType.LONGS; import static compiler.lib.template_framework.library.PrimitiveType.FLOATS; -import static compiler.lib.template_framework.library.PrimitiveType.FLOAT16S; import static compiler.lib.template_framework.library.PrimitiveType.DOUBLES; import static compiler.lib.template_framework.library.PrimitiveType.BOOLEANS; import static compiler.lib.template_framework.library.Float16Type.FLOAT16; @@ -41,6 +40,10 @@ import static compiler.lib.template_framework.library.CodeGenerationDataNameType.INTEGRAL_TYPES; import static compiler.lib.template_framework.library.CodeGenerationDataNameType.FLOATING_TYPES; import static compiler.lib.template_framework.library.CodeGenerationDataNameType.INT_LONG_TYPES; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.VECTOR_ELEMENT_TYPES; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.INTEGRAL_VECTOR_ELEMENT_TYPES; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.FLOATING_VECTOR_ELEMENT_TYPES; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.INT_LONG_VECTOR_ELEMENT_TYPES; /** * This class provides various lists of {@link Expression}s, that represent Java operators or library @@ -107,17 +110,11 @@ private static List generatePrimitiveOperations() { }); CodeGenerationDataNameType.FLOATING_TYPES.stream().forEach(type -> { - // float16 uses short as its carrier type; Java promotes short arithmetic - // results to int, so all arithmetic expressions need an explicit (short) cast. - boolean needsCast = (type == FLOAT16S); - String cb = needsCast ? "((short)(" : "("; - String ce = needsCast ? "))" : ")"; - // Arithmetic operators - ops.add(Expression.make(type, cb + "-(", type, ")" + ce)); - ops.add(Expression.make(type, cb, type, " + ", type, ce)); - ops.add(Expression.make(type, cb, type, " - ", type, ce)); - ops.add(Expression.make(type, cb, type, " * ", type, ce)); + ops.add(Expression.make(type, "(-(", type, "))")); + ops.add(Expression.make(type, "(", type, " + ", type, ")")); + ops.add(Expression.make(type, "(", type, " - ", type, ")")); + ops.add(Expression.make(type, "(", type, " * ", type, ")")); // Because of subtyping, we can sample an expression like `(float)((int)(3) / (int)(0))`. Floating point // division and modulo do not throw an ArithmeticException on division by zero, integer division and modulo // do. In the expression above, the division has an integer on both sides, so it is executed as an integer @@ -125,14 +122,10 @@ private static List generatePrimitiveOperations() { // To prevent this issue, we provide two versions of floating point division operations: one that casts // its operands and one that expects that an ArithmeticException might be thrown when we get unlucky when // sampling subtypes. - // Per-operand casts don't help float16 since its carrier type (short) always - // uses integer division regardless of casts. - if (!needsCast) { - ops.add(Expression.make(type, "((" + type.name() + ")(", type, ") / (" + type.name() +")(", type, "))")); - ops.add(Expression.make(type, "((" + type.name() + ")(", type, ") % (" + type.name() +")(", type, "))")); - } - ops.add(Expression.make(type, cb, type, " / ", type, ce, WITH_ARITHMETIC_EXCEPTION)); - ops.add(Expression.make(type, cb, type, " % ", type, ce, WITH_ARITHMETIC_EXCEPTION)); + ops.add(Expression.make(type, "((" + type.name() + ")(", type, ") / (" + type.name() +")(", type, "))")); + ops.add(Expression.make(type, "((" + type.name() + ")(", type, ") % (" + type.name() +")(", type, "))")); + ops.add(Expression.make(type, "(", type, " / ", type, ")", WITH_ARITHMETIC_EXCEPTION)); + ops.add(Expression.make(type, "(", type, " % ", type, ")", WITH_ARITHMETIC_EXCEPTION)); // Relational / Comparison Operators ops.add(Expression.make(BOOLEANS, "(", type, " == ", type, ")")); @@ -333,8 +326,11 @@ private enum VOPType { INTEGRAL_ASSOCIATIVE, // Binary - but only safe for integral reductions TERNARY } - private record VOP(String name, VOPType type, List elementTypes, boolean isDeterministic) { - VOP(String name, VOPType type, List elementTypes) { + // VOP element type pools are typed as VectorElementType so they can include + // Float16VectorType.FLOAT16 (the Float16Vector lane type) alongside the + // primitive lane types. + private record VOP(String name, VOPType type, List elementTypes, boolean isDeterministic) { + VOP(String name, VOPType type, List elementTypes) { this(name, type, elementTypes, true); } } @@ -344,81 +340,81 @@ private record VOP(String name, VOPType type, List elementTypes, // But if a test is just interested in determinism, they are still // non-deterministic. private static final List VECTOR_OPS = List.of( - new VOP("ABS", VOPType.UNARY, PRIMITIVE_TYPES), - new VOP("ACOS", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("ADD", VOPType.INTEGRAL_ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("AND", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("AND_NOT", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("ASHR", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("ASIN", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("ATAN", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("ATAN2", VOPType.BINARY, FLOATING_TYPES, false), // 2 ulp - new VOP("BIT_COUNT", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("BITWISE_BLEND", VOPType.TERNARY, INTEGRAL_TYPES), - new VOP("CBRT", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("COMPRESS_BITS", VOPType.BINARY, INT_LONG_TYPES), - new VOP("COS", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("COSH", VOPType.UNARY, FLOATING_TYPES, false), // 2.5 ulp - new VOP("DIV", VOPType.BINARY, FLOATING_TYPES), - new VOP("EXP", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("EXPAND_BITS", VOPType.BINARY, INT_LONG_TYPES), - new VOP("EXPM1", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("FIRST_NONZERO", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("FMA", VOPType.TERNARY, FLOATING_TYPES), - new VOP("HYPOT", VOPType.BINARY, FLOATING_TYPES, false), // 1.5 ulp - new VOP("LEADING_ZEROS_COUNT", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("LOG", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("LOG10", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("LOG1P", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("LSHL", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("LSHR", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("MIN", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("MAX", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("MUL", VOPType.INTEGRAL_ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("NEG", VOPType.UNARY, PRIMITIVE_TYPES), - new VOP("NOT", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("OR", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("POW", VOPType.BINARY, FLOATING_TYPES, false), // 1 ulp - new VOP("REVERSE", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("REVERSE_BYTES", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("ROL", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("ROR", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("SADD", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("SIN", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("SINH", VOPType.UNARY, FLOATING_TYPES, false), // 2.5 ulp - new VOP("SQRT", VOPType.UNARY, FLOATING_TYPES), - new VOP("SSUB", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("SUADD", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("SUB", VOPType.BINARY, PRIMITIVE_TYPES), - new VOP("SUSUB", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("TAN", VOPType.UNARY, FLOATING_TYPES, false), // 1.25 ulp - new VOP("TANH", VOPType.UNARY, FLOATING_TYPES, false), // 2.5 ulp - new VOP("TRAILING_ZEROS_COUNT", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("UMAX", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("UMIN", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("XOR", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("ZOMO", VOPType.UNARY, INTEGRAL_TYPES) + new VOP("ABS", VOPType.UNARY, VECTOR_ELEMENT_TYPES), + new VOP("ACOS", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("ADD", VOPType.INTEGRAL_ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("AND", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("AND_NOT", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ASHR", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ASIN", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("ATAN", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("ATAN2", VOPType.BINARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 2 ulp + new VOP("BIT_COUNT", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("BITWISE_BLEND", VOPType.TERNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("CBRT", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("COMPRESS_BITS", VOPType.BINARY, INT_LONG_VECTOR_ELEMENT_TYPES), + new VOP("COS", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("COSH", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 2.5 ulp + new VOP("DIV", VOPType.BINARY, FLOATING_VECTOR_ELEMENT_TYPES), + new VOP("EXP", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("EXPAND_BITS", VOPType.BINARY, INT_LONG_VECTOR_ELEMENT_TYPES), + new VOP("EXPM1", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("FIRST_NONZERO", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("FMA", VOPType.TERNARY, FLOATING_VECTOR_ELEMENT_TYPES), + new VOP("HYPOT", VOPType.BINARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1.5 ulp + new VOP("LEADING_ZEROS_COUNT", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("LOG", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("LOG10", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("LOG1P", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("LSHL", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("LSHR", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("MIN", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("MAX", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("MUL", VOPType.INTEGRAL_ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("NEG", VOPType.UNARY, VECTOR_ELEMENT_TYPES), + new VOP("NOT", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("OR", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("POW", VOPType.BINARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("REVERSE", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("REVERSE_BYTES", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ROL", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ROR", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("SADD", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("SIN", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("SINH", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 2.5 ulp + new VOP("SQRT", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES), + new VOP("SSUB", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("SUADD", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("SUB", VOPType.BINARY, VECTOR_ELEMENT_TYPES), + new VOP("SUSUB", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("TAN", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1.25 ulp + new VOP("TANH", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 2.5 ulp + new VOP("TRAILING_ZEROS_COUNT", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("UMAX", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("UMIN", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("XOR", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ZOMO", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES) ); private static final List VECTOR_CMP = List.of( - new VOP("EQ", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("GE", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("GT", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("LE", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("LT", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("NE", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("UGE", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("UGT", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("ULE", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("ULT", VOPType.ASSOCIATIVE, INTEGRAL_TYPES) + new VOP("EQ", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("GE", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("GT", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("LE", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("LT", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("NE", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("UGE", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("UGT", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ULE", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ULT", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES) ); private static final List VECTOR_TEST = List.of( - new VOP("IS_DEFAULT", VOPType.UNARY, PRIMITIVE_TYPES), - new VOP("IS_NEGATIVE", VOPType.UNARY, PRIMITIVE_TYPES), - new VOP("IS_FINITE", VOPType.UNARY, FLOATING_TYPES), - new VOP("IS_NAN", VOPType.UNARY, FLOATING_TYPES), - new VOP("IS_INFINITE", VOPType.UNARY, FLOATING_TYPES) + new VOP("IS_DEFAULT", VOPType.UNARY, VECTOR_ELEMENT_TYPES), + new VOP("IS_NEGATIVE", VOPType.UNARY, VECTOR_ELEMENT_TYPES), + new VOP("IS_FINITE", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES), + new VOP("IS_NAN", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES), + new VOP("IS_INFINITE", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES) ); // TODO: Conversion VectorOperators -> convertShape @@ -483,14 +479,14 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofCast(" - + type2.elementType.className() + ".class, " - + type.elementType.className() + ".class), 0))")); + + type2.elementType.elementTypeName() + ".class, " + + type.elementType.elementTypeName() + ".class), 0))")); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofCast(" - + type2.elementType.className() + ".class, " - + type.elementType.className() + ".class),", + + type2.elementType.elementTypeName() + ".class, " + + type.elementType.elementTypeName() + ".class),", INTS, // part "))", WITH_OUT_OF_BOUNDS_EXCEPTION)); } @@ -505,14 +501,14 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.className() + ".class, " - + type.elementType.className() + ".class), 0))", reinterpretInfo)); + + type2.elementType.elementTypeName() + ".class, " + + type.elementType.elementTypeName() + ".class), 0))", reinterpretInfo)); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.className() + ".class, " - + type.elementType.className() + ".class),", + + type2.elementType.elementTypeName() + ".class, " + + type.elementType.elementTypeName() + ".class),", INTS, // part "))", reinterpretInfo.combineWith(WITH_OUT_OF_BOUNDS_EXCEPTION))); if (type.elementType == BYTES) { @@ -530,7 +526,7 @@ private static List generateVectorOperations() { if (type.elementType == FLOATS) { ops.add(Expression.make(type, "", type2, ".reinterpretAsFloats()", reinterpretInfo)); } - if (type.elementType == FLOAT16S) { + if (type.elementType instanceof Float16VectorType) { ops.add(Expression.make(type, "", type2, ".reinterpretAsFloat16s()", reinterpretInfo)); } if (type.elementType == DOUBLES) { @@ -568,8 +564,8 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofCast(" - + type2.elementType.className() + ".class, " - + type.elementType.className() + ".class), " + + type2.elementType.elementTypeName() + ".class, " + + type.elementType.elementTypeName() + ".class), " + type.speciesName + ", ", INTS, // part "))", WITH_OUT_OF_BOUNDS_EXCEPTION)); @@ -577,8 +573,8 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.className() + ".class, " - + type.elementType.className() + ".class), " + + type2.elementType.elementTypeName() + ".class, " + + type.elementType.elementTypeName() + ".class), " + type.speciesName + ", ", INTS, // part "))", reinterpretInfo.combineWith(WITH_OUT_OF_BOUNDS_EXCEPTION))); @@ -595,16 +591,16 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofCast(" - + type2.elementType.className() + ".class, " - + type.elementType.className() + ".class), " + + type2.elementType.elementTypeName() + ".class, " + + type.elementType.elementTypeName() + ".class), " + type.speciesName + ", ", INTS, " & " + partMask + "))")); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.className() + ".class, " - + type.elementType.className() + ".class), " + + type2.elementType.elementTypeName() + ".class, " + + type.elementType.elementTypeName() + ".class), " + type.speciesName + ", ", INTS, " & " + partMask + "))", reinterpretInfo)); } else { @@ -614,16 +610,16 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofCast(" - + type2.elementType.className() + ".class, " - + type.elementType.className() + ".class), " + + type2.elementType.elementTypeName() + ".class, " + + type.elementType.elementTypeName() + ".class), " + type.speciesName + ", " + "-(", INTS, " & " + partMask + ")))")); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.className() + ".class, " - + type.elementType.className() + ".class), " + + type2.elementType.elementTypeName() + ".class, " + + type.elementType.elementTypeName() + ".class), " + type.speciesName + ", " + "-(", INTS, " & " + partMask + ")))", reinterpretInfo)); } @@ -848,8 +844,18 @@ private static List ternaryOps(Operations.VOP vop, VectorType.Vector FLOAT16_OPERATIONS ); + /** + * Provides a list of Vector API operations. Iterates over all + * {@link CodeGenerationDataNameType#VECTOR_VECTOR_TYPES}, including + * {@code Float16Vector_*}, whose lanes are described by + * {@link Float16VectorType#FLOAT16}. + */ public static final List VECTOR_OPERATIONS = generateVectorOperations(); + /** + * Provides a list of all operations: every scalar operation and every + * Vector API operation. + */ public static final List ALL_OPERATIONS = Utils.concat( SCALAR_NUMERIC_OPERATIONS, VECTOR_OPERATIONS diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java index 377809ea39d2d..c6729e006c537 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java @@ -40,8 +40,20 @@ * The {@link PrimitiveType} models Java's primitive types, and provides a set * of useful methods for code generation, such as the {@link #byteSize} and * {@link #boxedTypeName}. + * + *

{@link PrimitiveType} is a Java scalar type and additionally + * doubles as a {@link VectorElementType} for those Vector API lane types whose + * lane carrier is itself a Java primitive (e.g. {@code IntVector}'s lane + * carrier is {@code int}). For these primitive lane types + * {@link #carrierTypeName} and {@link #elementTypeName} both coincide with + * {@link #name}. + * + *

Non-primitive lane types, such as the {@code Float16Vector} lane, are + * modeled by separate {@link VectorElementType} implementations (see + * {@link Float16VectorType}). They do not appear in any of + * the scalar {@code PRIMITIVE_TYPES}/{@code FLOATING_TYPES} lists. */ -public final class PrimitiveType implements CodeGenerationDataNameType { +public final class PrimitiveType implements VectorElementType { 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); @@ -50,9 +62,8 @@ public final class PrimitiveType implements CodeGenerationDataNameType { 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(); - private static final Generator GEN_FLOAT16 = Generators.G.float16s(); - private static enum Kind { BYTE, SHORT, CHAR, INT, LONG, FLOAT, DOUBLE, BOOLEAN, FLOAT16 }; + private static enum Kind { BYTE, SHORT, CHAR, INT, LONG, FLOAT, DOUBLE, BOOLEAN }; // We have one static instance each, so we do not have duplicated instances. static final PrimitiveType BYTES = new PrimitiveType(Kind.BYTE ); @@ -63,7 +74,6 @@ private static enum Kind { BYTE, SHORT, CHAR, INT, LONG, FLOAT, DOUBLE, BOOLEAN, static final PrimitiveType FLOATS = new PrimitiveType(Kind.FLOAT ); static final PrimitiveType DOUBLES = new PrimitiveType(Kind.DOUBLE ); static final PrimitiveType BOOLEANS = new PrimitiveType(Kind.BOOLEAN); - static final PrimitiveType FLOAT16S = new PrimitiveType(Kind.FLOAT16); final Kind kind; @@ -106,39 +116,17 @@ public String name() { case FLOAT -> "float"; case DOUBLE -> "double"; case BOOLEAN -> "boolean"; - case FLOAT16 -> "float16"; }; } - /** - * Returns the Vector lane carrier type name. For most types this is the - * same as {@link #name()}, but for {@code float16} the carrier type is - * {@code short}. - */ - public String cname() { - return switch (kind) { - case BYTE -> "byte"; - case SHORT -> "short"; - case CHAR -> "char"; - case INT -> "int"; - case LONG -> "long"; - case FLOAT -> "float"; - case DOUBLE -> "double"; - case BOOLEAN -> "boolean"; - case FLOAT16 -> "short"; - }; + @Override + public String carrierTypeName() { + return name(); } - /** - * Returns the name used in {@code .class} literals. For Java primitives - * this is the primitive keyword (e.g. {@code int}). For {@code float16} - * it is {@code Float16} since there is no {@code float16} keyword. - */ - public String className() { - return switch (kind) { - case FLOAT16 -> "Float16"; - default -> name(); - }; + @Override + public String elementTypeName() { + return name(); } @Override @@ -152,7 +140,6 @@ public Object con() { case BYTE -> "(byte)" + GEN_BYTE.next(); case SHORT -> "(short)" + GEN_SHORT.next(); case CHAR -> "(char)" + GEN_CHAR.next(); - case FLOAT16 -> "(short)" + GEN_FLOAT16.next(); case INT -> GEN_INT.next(); case LONG -> GEN_LONG.next(); case FLOAT -> GEN_FLOAT.next(); @@ -167,10 +154,11 @@ public Object con() { * @return Size of the type in bytes. * @throws UnsupportedOperationException for boolean which has no defined size. */ + @Override public int byteSize() { return switch (kind) { case BYTE -> 1; - case SHORT, CHAR, FLOAT16 -> 2; + case SHORT, CHAR -> 2; case INT, FLOAT -> 4; case LONG, DOUBLE -> 8; case BOOLEAN -> { throw new UnsupportedOperationException("boolean does not have a defined 'size'"); } @@ -182,6 +170,7 @@ public int byteSize() { * * @return the name of the boxed type. */ + @Override public String boxedTypeName() { return switch (kind) { case BYTE -> "Byte"; @@ -192,7 +181,6 @@ public String boxedTypeName() { case FLOAT -> "Float"; case DOUBLE -> "Double"; case BOOLEAN -> "Boolean"; - case FLOAT16 -> "Float16"; }; } @@ -205,7 +193,6 @@ public String fieldDesc() { return switch (kind) { case LONG -> "J"; case BOOLEAN -> "Z"; - case FLOAT16 -> "S"; default -> boxedTypeName().substring(0, 1); }; } @@ -231,10 +218,11 @@ public String abbrev() { * * @return true iff the type is a floating point type. */ + @Override public boolean isFloating() { return switch (kind) { case BYTE, SHORT, CHAR, INT, LONG, BOOLEAN -> false; - case FLOAT16, FLOAT, DOUBLE -> true; + case FLOAT, DOUBLE -> true; }; } @@ -250,6 +238,7 @@ public boolean isFloating() { * @return the token representing the method call to obtain a * random value for the given type at runtime. */ + @Override public Object callLibraryRNG() { return switch (kind) { case BYTE -> "LibraryRNG.nextByte()"; @@ -260,7 +249,6 @@ public Object callLibraryRNG() { case FLOAT -> "LibraryRNG.nextFloat()"; case DOUBLE -> "LibraryRNG.nextDouble()"; case BOOLEAN -> "LibraryRNG.nextBoolean()"; - case FLOAT16 -> "LibraryRNG.nextFloat16()"; }; } @@ -269,6 +257,12 @@ public Object callLibraryRNG() { * random number generators available, wrapping {@link Generators}. This * is supposed to be used in tandem with {@link #callLibraryRNG}. * + *

In addition to the Java primitive generators, this also emits + * helpers for {@code Float16Vector}'s {@code short} carrier + * ({@code nextFloat16()} / {@code fill_float16(short[])}) so that + * {@link Float16VectorType#callLibraryRNG()} can be used with vector + * fuzzers without depending on this class importing Float16Vector itself. + * * Note: you must ensure that all required imports are performed: * {@code java.util.Random} * {@code jdk.test.lib.Utils} @@ -322,6 +316,7 @@ public static boolean nextBoolean() { return RANDOM.nextBoolean(); } + // Float16Vector lane helpers. Float16 lanes are carried in short[]. public static short nextFloat16() { return GEN_FLOAT16.next(); } @@ -333,9 +328,7 @@ public static void fill_float16(short[] a) { } """, - CodeGenerationDataNameType.PRIMITIVE_TYPES.stream() - .filter(type -> !type.cname().equals("short") || type.name().equals("short")) - .map(type -> scope( + CodeGenerationDataNameType.PRIMITIVE_TYPES.stream().map(type -> scope( let("type", type), """ public static void fill(#type[] a) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorElementType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorElementType.java new file mode 100644 index 0000000000000..66bae274e9eb3 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorElementType.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.template_framework.library; + +/** + * A {@link VectorElementType} describes a single lane-element of a Vector API + * vector ({@link VectorType.Vector}). It abstracts over: + *

    + *
  • {@link PrimitiveType} - the standard Java primitive lane types + * (byte, short, int, long, float, double, and char/boolean where + * applicable). For these the carrier and element-class names coincide + * with the primitive keyword (e.g. {@code int} / {@code Integer.class}).
  • + *
  • {@link Float16VectorType} - the {@code Float16Vector} lane type. Float16 + * has no Java primitive keyword; its lanes are stored in a {@code short[]} + * carrier and identified by {@code Float16.class} in + * {@code VectorOperators.Conversion.ofCast}/{@code ofReinterpret} + * expressions.
  • + *
+ * + *

This interface intentionally lives outside the scalar + * {@link PrimitiveType} type lists (e.g. {@code PRIMITIVE_TYPES}, + * {@code FLOATING_TYPES}). Those lists model Java scalar types and are consumed + * by scalar fuzzers. Vector-lane lists (e.g. {@code VECTOR_ELEMENT_TYPES}, + * {@code FLOATING_VECTOR_ELEMENT_TYPES}) are typed as {@code List} + * and may include {@link Float16VectorType#FLOAT16}. + */ +public interface VectorElementType extends CodeGenerationDataNameType { + + /** + * @return The logical name of the lane type (e.g. {@code "int"}, + * {@code "float16"}). + */ + @Override + String name(); + + /** + * @return The element type of the Java carrier array used to hold these + * lanes when calling {@code fromArray}/{@code intoArray}. For most + * lane types this is the same as {@link #name()}; for + * {@code float16} it is {@code "short"}. + */ + String carrierTypeName(); + + /** + * @return The element type token used as a {@code .class} literal in + * {@code VectorOperators.Conversion.ofCast}/{@code ofReinterpret} + * expressions. For Java primitives this is the primitive keyword + * (e.g. {@code "int"}). For {@code float16} this is + * {@code "Float16"}. + */ + String elementTypeName(); + + /** + * @return The boxed type name used to parameterize generic types such as + * {@code VectorMask} and {@code VectorShuffle} + * (e.g. {@code "Integer"}, {@code "Float16"}). + */ + String boxedTypeName(); + + /** + * @return Size of the lane type in bytes. + */ + int byteSize(); + + /** + * @return {@code true} iff the lane type is a floating point type. + */ + boolean isFloating(); + + /** + * @return A token representing a call to the corresponding pseudo random + * number generator from {@link PrimitiveType#generateLibraryRNG()}. + */ + Object callLibraryRNG(); +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java index ff09889f7591c..1e8d21ea05df9 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java @@ -36,10 +36,14 @@ import static compiler.lib.template_framework.library.PrimitiveType.FLOATS; import static compiler.lib.template_framework.library.PrimitiveType.DOUBLES; import static compiler.lib.template_framework.library.PrimitiveType.BOOLEANS; -import static compiler.lib.template_framework.library.PrimitiveType.FLOAT16S; /** * The {@link VectorType} models the Vector API types. + * + *

A {@code VectorType.Vector} is parameterized by a {@link VectorElementType} + * (its lane element type) and a lane count. The lane element type may be a + * Java primitive lane ({@link PrimitiveType}) or {@link Float16VectorType} for + * {@code Float16Vector}. */ public abstract class VectorType implements CodeGenerationDataNameType { private static final Random RANDOM = Utils.getRandomInstance(); @@ -74,10 +78,10 @@ public abstract class VectorType implements CodeGenerationDataNameType { public static final VectorType.Vector DOUBLE_256 = new VectorType.Vector(DOUBLES, 4); public static final VectorType.Vector DOUBLE_512 = new VectorType.Vector(DOUBLES, 8); - public static final VectorType.Vector FLOAT16_64 = new VectorType.Vector(FLOAT16S, 4); - public static final VectorType.Vector FLOAT16_128 = new VectorType.Vector(FLOAT16S, 8); - public static final VectorType.Vector FLOAT16_256 = new VectorType.Vector(FLOAT16S, 16); - public static final VectorType.Vector FLOAT16_512 = new VectorType.Vector(FLOAT16S, 32); + public static final VectorType.Vector FLOAT16_64 = new VectorType.Vector(Float16VectorType.FLOAT16, 4); + public static final VectorType.Vector FLOAT16_128 = new VectorType.Vector(Float16VectorType.FLOAT16, 8); + public static final VectorType.Vector FLOAT16_256 = new VectorType.Vector(Float16VectorType.FLOAT16, 16); + public static final VectorType.Vector FLOAT16_512 = new VectorType.Vector(Float16VectorType.FLOAT16, 32); private final String vectorTypeName; @@ -101,7 +105,7 @@ public boolean isSubtypeOf(DataName.Type other) { return this == other; } - private static final String vectorTypeName(PrimitiveType elementType) { + private static final String vectorTypeName(VectorElementType elementType) { return switch(elementType.name()) { case "byte" -> "ByteVector"; case "short" -> "ShortVector"; @@ -116,14 +120,14 @@ private static final String vectorTypeName(PrimitiveType elementType) { } public static final class Vector extends VectorType { - public final PrimitiveType elementType; + public final VectorElementType elementType; public final int length; // lane count public final String speciesName; public final Mask maskType; public final Shuffle shuffleType; - private Vector(PrimitiveType elementType, int length) { + private Vector(VectorElementType elementType, int length) { super(vectorTypeName(elementType)); this.elementType = elementType; this.length = length; @@ -139,7 +143,7 @@ public final Object con() { return List.of(name(), ".zero(", speciesName, ")"); } else if (r <= 8) { return List.of( - name(), ".fromArray(", speciesName, ", new ", elementType.cname(), "[] {", + name(), ".fromArray(", speciesName, ", new ", elementType.carrierTypeName(), "[] {", elementType.con(), Stream.generate(() -> List.of(", ", elementType.con()) diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java b/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java index 8df8bac8f2c75..6f1eb2c1c1df5 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java @@ -22,16 +22,15 @@ */ /* - * @test id=AVX2 + * @test * @bug 8369699 * @key randomness * @summary Test the Template Library's expression generation for the Vector API. - * @requires os.family == "linux" & os.simpleArch == "x64" * @modules jdk.incubator.vector * @modules java.base/jdk.internal.misc * @library /test/lib / * @compile ../../compiler/lib/verify/Verify.java - * @run driver ${test.main.class} -XX:UseAVX=3 + * @run driver ${test.main.class} */ // TODO: remove the x64 and linux restriction above. I added that for now so we are not flooded @@ -75,6 +74,8 @@ import compiler.lib.template_framework.library.Operations; import compiler.lib.template_framework.library.TestFrameworkClass; import compiler.lib.template_framework.library.PrimitiveType; +import compiler.lib.template_framework.library.Float16VectorType; +import compiler.lib.template_framework.library.VectorElementType; import compiler.lib.template_framework.library.VectorType; /** @@ -171,23 +172,38 @@ public static String generate(CompileFramework comp) { // - We check correctness with a reference method that does the same but runs in the interpreter. // - Input values are delivered via fields or array loads. // - The final vector is written into an array, and that array is returned. - var template2Body = Template.make("expression", "arguments", (Expression expression, List arguments) -> scope( - let("elementType", ((VectorType.Vector)expression.returnType).elementType.cname()), - """ - try { - #elementType[] out = new #elementType[1000]; - """, - expression.asToken(arguments), ".intoArray(out, 0);\n", - "return out;\n", - expression.info.exceptions.stream().map(exception -> - "} catch (" + exception + " e) { return e;\n" - ).toList(), - """ - } finally { - // Just javac is happy if there are no exceptions to catch. - } - """ - )); + // + // NaN canonicalization (Float16Vector only): the {@code short} carrier of Float16Vector lanes + // distinguishes multiple NaN bit patterns, so a structural comparison between two distinct NaN + // bit patterns would spuriously fail. We widen the {@code short[]} carrier to {@code float[]} + // via {@link Float#float16ToFloat}, which returns a canonical NaN for any NaN input. + var template2Body = Template.make("expression", "arguments", (Expression expression, List arguments) -> { + VectorType.Vector retType = (VectorType.Vector) expression.returnType; + boolean float16Result = retType.elementType instanceof Float16VectorType; + return scope( + let("carrierType", retType.elementType.carrierTypeName()), + """ + try { + #carrierType[] out = new #carrierType[1000]; + """, + expression.asToken(arguments), ".intoArray(out, 0);\n", + float16Result + ? List.of( + "// Float16Vector NaN canonicalization: widen short carrier to float for compare.\n", + "float[] outF = new float[out.length];\n", + "for (int i = 0; i < out.length; i++) { outF[i] = Float.float16ToFloat(out[i]); }\n", + "return outF;\n") + : "return out;\n", + expression.info.exceptions.stream().map(exception -> + "} catch (" + exception + " e) { return e;\n" + ).toList(), + """ + } finally { + // Just javac is happy if there are no exceptions to catch. + } + """ + ); + }); var template2 = Template.make("type", (VectorType.Vector type) -> { // The depth determines roughly how many operations are going to be used in the expression. @@ -211,7 +227,9 @@ public static String generate(CompileFramework comp) { } case 1 -> { // Create the constant outside, and pass it. - String typeName = (argumentType instanceof PrimitiveType pt) ? pt.cname() : argumentType.name(); + String typeName = (argumentType instanceof VectorElementType vet) + ? vet.carrierTypeName() + : argumentType.name(); arguments.add(new TestArgument( List.of(typeName, " ", name, " = ", argumentType.con(), ";\n"), name, @@ -220,25 +238,25 @@ public static String generate(CompileFramework comp) { )); } default -> { - if (argumentType instanceof PrimitiveType t) { + if (argumentType instanceof VectorElementType vet) { // We can use the LibraryRGN to create a new value for the primitive in each // invocation. We have to make sure to call the LibraryRNG in the "defineAndFill", // so we get the same value for both test and reference. If we called LibraryRNG // for "use", we would get separate values, which is not helpful. arguments.add(new TestArgument( - List.of(t.cname(), " ", name, " = ", t.callLibraryRNG(), ";\n"), + List.of(vet.carrierTypeName(), " ", name, " = ", vet.callLibraryRNG(), ";\n"), name, - List.of(t.cname(), " ", name), + List.of(vet.carrierTypeName(), " ", name), name )); } else if (argumentType instanceof VectorType.Vector t) { - PrimitiveType et = t.elementType; - String fillMethod = et.name().equals("float16") ? "fill_float16" : "fill"; + VectorElementType et = t.elementType; + String fillMethod = (et instanceof Float16VectorType) ? "fill_float16" : "fill"; arguments.add(new TestArgument( - List.of(et.cname(), "[] ", name, " = new ", et.cname(), "[1000];\n", + List.of(et.carrierTypeName(), "[] ", name, " = new ", et.carrierTypeName(), "[1000];\n", "LibraryRNG.", fillMethod, "(", name,");\n"), name, - List.of(et.cname(), "[] ", name), + List.of(et.carrierTypeName(), "[] ", name), List.of(t.name(), ".fromArray(", t.speciesName, ", ", name, ", 0)") )); } else if (argumentType instanceof VectorType.Mask t) { From 4a817aa080cdb60d7c7ebb35e7f6df30dccc759d Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Tue, 2 Jun 2026 08:23:23 +0000 Subject: [PATCH 3/3] Review comments resolution --- .../library/Float16VectorType.java | 7 +--- .../library/Operations.java | 40 +++++++++---------- .../library/PrimitiveType.java | 8 +--- .../library/VectorElementType.java | 29 ++++---------- .../library/VectorType.java | 2 +- 5 files changed, 30 insertions(+), 56 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Float16VectorType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Float16VectorType.java index 9865080a7eff2..900f8432ebaee 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Float16VectorType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Float16VectorType.java @@ -63,7 +63,7 @@ public boolean isSubtypeOf(DataName.Type other) { @Override public String name() { - return "float16"; + return "Float16"; } @Override @@ -71,11 +71,6 @@ public String carrierTypeName() { return "short"; } - @Override - public String elementTypeName() { - return "Float16"; - } - @Override public String boxedTypeName() { return "Float16"; diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index 5dd5998b6d464..29c0d98aacc83 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -479,14 +479,14 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofCast(" - + type2.elementType.elementTypeName() + ".class, " - + type.elementType.elementTypeName() + ".class), 0))")); + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), 0))")); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofCast(" - + type2.elementType.elementTypeName() + ".class, " - + type.elementType.elementTypeName() + ".class),", + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class),", INTS, // part "))", WITH_OUT_OF_BOUNDS_EXCEPTION)); } @@ -501,14 +501,14 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.elementTypeName() + ".class, " - + type.elementType.elementTypeName() + ".class), 0))", reinterpretInfo)); + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), 0))", reinterpretInfo)); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.elementTypeName() + ".class, " - + type.elementType.elementTypeName() + ".class),", + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class),", INTS, // part "))", reinterpretInfo.combineWith(WITH_OUT_OF_BOUNDS_EXCEPTION))); if (type.elementType == BYTES) { @@ -564,8 +564,8 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofCast(" - + type2.elementType.elementTypeName() + ".class, " - + type.elementType.elementTypeName() + ".class), " + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), " + type.speciesName + ", ", INTS, // part "))", WITH_OUT_OF_BOUNDS_EXCEPTION)); @@ -573,8 +573,8 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.elementTypeName() + ".class, " - + type.elementType.elementTypeName() + ".class), " + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), " + type.speciesName + ", ", INTS, // part "))", reinterpretInfo.combineWith(WITH_OUT_OF_BOUNDS_EXCEPTION))); @@ -591,16 +591,16 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofCast(" - + type2.elementType.elementTypeName() + ".class, " - + type.elementType.elementTypeName() + ".class), " + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), " + type.speciesName + ", ", INTS, " & " + partMask + "))")); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.elementTypeName() + ".class, " - + type.elementType.elementTypeName() + ".class), " + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), " + type.speciesName + ", ", INTS, " & " + partMask + "))", reinterpretInfo)); } else { @@ -610,16 +610,16 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofCast(" - + type2.elementType.elementTypeName() + ".class, " - + type.elementType.elementTypeName() + ".class), " + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), " + type.speciesName + ", " + "-(", INTS, " & " + partMask + ")))")); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.elementTypeName() + ".class, " - + type.elementType.elementTypeName() + ".class), " + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), " + type.speciesName + ", " + "-(", INTS, " & " + partMask + ")))", reinterpretInfo)); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java index c6729e006c537..ddce36ddfa04c 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java @@ -45,8 +45,7 @@ * doubles as a {@link VectorElementType} for those Vector API lane types whose * lane carrier is itself a Java primitive (e.g. {@code IntVector}'s lane * carrier is {@code int}). For these primitive lane types - * {@link #carrierTypeName} and {@link #elementTypeName} both coincide with - * {@link #name}. + * {@link #carrierTypeName} coincides with {@link #name}. * *

Non-primitive lane types, such as the {@code Float16Vector} lane, are * modeled by separate {@link VectorElementType} implementations (see @@ -124,11 +123,6 @@ public String carrierTypeName() { return name(); } - @Override - public String elementTypeName() { - return name(); - } - @Override public String toString() { return name(); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorElementType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorElementType.java index 66bae274e9eb3..289d68e5d1cc9 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorElementType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorElementType.java @@ -29,13 +29,14 @@ *

    *
  • {@link PrimitiveType} - the standard Java primitive lane types * (byte, short, int, long, float, double, and char/boolean where - * applicable). For these the carrier and element-class names coincide - * with the primitive keyword (e.g. {@code int} / {@code Integer.class}).
  • + * applicable). For these {@link #name()} is the primitive keyword + * (e.g. {@code "int"}) and {@code name() + ".class"} yields the + * primitive {@code Class} literal ({@code int.class}). *
  • {@link Float16VectorType} - the {@code Float16Vector} lane type. Float16 * has no Java primitive keyword; its lanes are stored in a {@code short[]} - * carrier and identified by {@code Float16.class} in - * {@code VectorOperators.Conversion.ofCast}/{@code ofReinterpret} - * expressions.
  • + * carrier and {@link #name()} returns {@code "Float16"} so that + * {@code name() + ".class"} ({@code Float16.class}) is the token expected + * by {@code VectorOperators.Conversion.ofCast}/{@code ofReinterpret}. *
* *

This interface intentionally lives outside the scalar @@ -47,30 +48,14 @@ */ public interface VectorElementType extends CodeGenerationDataNameType { - /** - * @return The logical name of the lane type (e.g. {@code "int"}, - * {@code "float16"}). - */ - @Override - String name(); - /** * @return The element type of the Java carrier array used to hold these * lanes when calling {@code fromArray}/{@code intoArray}. For most * lane types this is the same as {@link #name()}; for - * {@code float16} it is {@code "short"}. + * {@code Float16} it is {@code "short"}. */ String carrierTypeName(); - /** - * @return The element type token used as a {@code .class} literal in - * {@code VectorOperators.Conversion.ofCast}/{@code ofReinterpret} - * expressions. For Java primitives this is the primitive keyword - * (e.g. {@code "int"}). For {@code float16} this is - * {@code "Float16"}. - */ - String elementTypeName(); - /** * @return The boxed type name used to parameterize generic types such as * {@code VectorMask} and {@code VectorShuffle} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java index 1e8d21ea05df9..710186df8c5c9 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java @@ -114,7 +114,7 @@ private static final String vectorTypeName(VectorElementType elementType) { case "long" -> "LongVector"; case "float" -> "FloatVector"; case "double" -> "DoubleVector"; - case "float16" -> "Float16Vector"; + case "Float16" -> "Float16Vector"; default -> throw new UnsupportedOperationException("Not supported: " + elementType.name()); }; }