Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
c8efd7f
JDK-8359412
eme64 Aug 21, 2025
1144adf
first use
eme64 Aug 26, 2025
d933e67
refactor
eme64 Aug 26, 2025
17d09c6
add a lot of operators
eme64 Aug 26, 2025
213a344
more operators
eme64 Aug 27, 2025
a7385d7
more testing and impl
eme64 Aug 27, 2025
bf8f7a7
wip
eme64 Aug 27, 2025
3e5d0cc
more nested testing
eme64 Aug 27, 2025
cc69147
fixes and info tests
eme64 Aug 27, 2025
c0ef0ef
wip expression fuzzer
eme64 Aug 27, 2025
e24bd84
some todos
eme64 Aug 27, 2025
f62bd45
route via MethodArgument
eme64 Aug 28, 2025
c0482d6
checksum
eme64 Aug 28, 2025
fb3c93a
wip rng
eme64 Sep 4, 2025
1d58358
LibraryRNG
eme64 Sep 5, 2025
3356f9a
handle non-deterministic results
eme64 Sep 5, 2025
d614861
test refactoring
eme64 Sep 5, 2025
37d4ae7
wip test cmp
eme64 Sep 9, 2025
aacfc37
add more comments
eme64 Sep 10, 2025
8ec054d
wip constraints
eme64 Sep 10, 2025
2e954ac
improve expression fuzzer
eme64 Sep 10, 2025
4bf6921
documentation
eme64 Sep 10, 2025
360e7f2
fix bug
eme64 Sep 10, 2025
b58ce8a
LibraryRNG example
eme64 Sep 10, 2025
0709731
fix whitespaces
eme64 Sep 11, 2025
d66aa98
Apply Manuel's suggestions part 1
eme64 Sep 18, 2025
05fb63c
Apply Manuel's suggestions part 2
eme64 Sep 18, 2025
0a269c3
Apply Manuel's suggestions part 3
eme64 Sep 18, 2025
a6f83b5
Merge branch 'master' into JDK-8359412-Template-Framework-Expressions
eme64 Sep 18, 2025
d39d2de
add othervm to test
eme64 Sep 18, 2025
c04c879
more comments
eme64 Sep 18, 2025
199e06a
Apply suggestions from code review
eme64 Oct 13, 2025
1f51d14
Merge branch 'master' into JDK-8359412-Template-Framework-Expressions
eme64 Oct 13, 2025
c6787e4
refactor for Christian
eme64 Oct 13, 2025
3e4a1b7
Update test/hotspot/jtreg/compiler/lib/template_framework/library/Ope…
eme64 Oct 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
349 changes: 349 additions & 0 deletions test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave it my best shot to suggest a reasonable and reasonably consistent alignment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wow, nice. Thanks for the work :)

Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package compiler.lib.template_framework.library;

import java.util.List;
import java.util.ArrayList;
import java.util.Set;

import static compiler.lib.template_framework.library.PrimitiveType.BYTES;
import static compiler.lib.template_framework.library.PrimitiveType.SHORTS;
import static compiler.lib.template_framework.library.PrimitiveType.CHARS;
import static compiler.lib.template_framework.library.PrimitiveType.INTS;
import static compiler.lib.template_framework.library.PrimitiveType.LONGS;
import static compiler.lib.template_framework.library.PrimitiveType.FLOATS;
import static compiler.lib.template_framework.library.PrimitiveType.DOUBLES;
import static compiler.lib.template_framework.library.PrimitiveType.BOOLEANS;

/**
* This class provides various lists of {@link Expression}s, that represent Java operators or library
* methods. For example, we represent arithmetic operations on primitive types.
*/
public final class Operations {

// private constructor to avoid instantiation.
private Operations() {}

/**
* Provides a lits of operations on {@link PrimitiveType}s, such as arithmetic, logical,
* and cast operations.
*/
public static final List<Expression> PRIMITIVE_OPERATIONS = generatePrimitiveOperations();

private static List<Expression> generatePrimitiveOperations() {
List<Expression> ops = new ArrayList<>();

Expression.Info withArithmeticException = new Expression.Info().withExceptions(Set.of("ArithmeticException"));
Expression.Info withNondeterministicResult = new Expression.Info().withNondeterministicResult();

// Cast between all primitive types. Except for Boolean, we cannot cast from and to.
CodeGenerationDataNameType.INTEGRAL_AND_FLOATING_TYPES.stream().forEach(src -> {
CodeGenerationDataNameType.INTEGRAL_AND_FLOATING_TYPES.stream().forEach(dst -> {
ops.add(Expression.make(dst, "(" + dst.name() + ")(", src, ")"));
});
});

// Ternary operator.
CodeGenerationDataNameType.INTEGRAL_AND_FLOATING_TYPES.stream().forEach(type -> {
ops.add(Expression.make(type, "(", BOOLEANS, "?", type, ":", type, ")"));
});

List.of(INTS, LONGS).stream().forEach(type -> {
// Arithmetic operators
ops.add(Expression.make(type, "(-(", type, "))"));
ops.add(Expression.make(type, "(", type, " + ", type, ")"));
ops.add(Expression.make(type, "(", type, " - ", type, ")"));
ops.add(Expression.make(type, "(", type, " * ", type, ")"));
ops.add(Expression.make(type, "(", type, " / ", type, ")", withArithmeticException));
ops.add(Expression.make(type, "(", type, " % ", type, ")", withArithmeticException));

// Bitwise Operators (non short-circuit)
ops.add(Expression.make(type, "(~(", type, "))"));
ops.add(Expression.make(type, "(", type, " & ", type, ")"));
ops.add(Expression.make(type, "(", type, " | ", type, ")"));
ops.add(Expression.make(type, "(", type, " ^ ", type, ")"));
ops.add(Expression.make(type, "(", type, " << ", type, ")"));
ops.add(Expression.make(type, "(", type, " >> ", type, ")"));
ops.add(Expression.make(type, "(", type, " >>> ", type, ")"));

// Relational / Comparison Operators
ops.add(Expression.make(BOOLEANS, "(", type, " == ", type, ")"));
ops.add(Expression.make(BOOLEANS, "(", type, " != ", type, ")"));
ops.add(Expression.make(BOOLEANS, "(", type, " > ", type, ")"));
ops.add(Expression.make(BOOLEANS, "(", type, " < ", type, ")"));
ops.add(Expression.make(BOOLEANS, "(", type, " >= ", type, ")"));
ops.add(Expression.make(BOOLEANS, "(", type, " <= ", type, ")"));
});

CodeGenerationDataNameType.FLOATING_TYPES.stream().forEach(type -> {
// Arithmetic operators
ops.add(Expression.make(type, "(-(", type, "))"));
ops.add(Expression.make(type, "(", type, " + ", type, ")"));
ops.add(Expression.make(type, "(", type, " - ", type, ")"));
ops.add(Expression.make(type, "(", type, " * ", type, ")"));
ops.add(Expression.make(type, "(", type, " / ", type, ")"));
ops.add(Expression.make(type, "(", type, " % ", type, ")"));

// Relational / Comparison Operators
ops.add(Expression.make(BOOLEANS, "(", type, " == ", type, ")"));
ops.add(Expression.make(BOOLEANS, "(", type, " != ", type, ")"));
ops.add(Expression.make(BOOLEANS, "(", type, " > ", type, ")"));
ops.add(Expression.make(BOOLEANS, "(", type, " < ", type, ")"));
ops.add(Expression.make(BOOLEANS, "(", type, " >= ", type, ")"));
ops.add(Expression.make(BOOLEANS, "(", type, " <= ", type, ")"));
});

// ------------ byte -------------
// Cast and ternary operator handled above.
// Arithmetic operations are not performed in byte, but rather promoted to int.

// ------------ Byte -------------
ops.add(Expression.make(INTS, "Byte.compare(", BYTES, ", ", BYTES, ")"));
ops.add(Expression.make(INTS, "Byte.compareUnsigned(", BYTES, ", ", BYTES, ")"));
ops.add(Expression.make(INTS, "Byte.toUnsignedInt(", BYTES, ")"));
ops.add(Expression.make(LONGS, "Byte.toUnsignedLong(", BYTES, ")"));

// ------------ char -------------
// Cast and ternary operator handled above.
// Arithmetic operations are not performned in char, but rather promoted to int.

// ------------ Character -------------
ops.add(Expression.make(INTS, "Character.compare(", CHARS, ", ", CHARS, ")"));
ops.add(Expression.make(CHARS, "Character.reverseBytes(", CHARS, ")"));

// ------------ short -------------
// Cast and ternary operator handled above.
// Arithmetic operations are not performned in short, but rather promoted to int.

// ------------ Short -------------
ops.add(Expression.make(INTS, "Short.compare(", SHORTS, ", ", SHORTS, ")"));
ops.add(Expression.make(INTS, "Short.compareUnsigned(", SHORTS, ", ", SHORTS, ")"));
ops.add(Expression.make(SHORTS, "Short.reverseBytes(", SHORTS, ")"));
ops.add(Expression.make(INTS, "Short.toUnsignedInt(", SHORTS, ")"));
ops.add(Expression.make(LONGS, "Short.toUnsignedLong(", SHORTS, ")"));

// ------------ int -------------
// Cast and ternary operator handled above.
// Arithmetic, Bitwise, Relational / Comparison handled above.

// ------------ Integer -------------
ops.add(Expression.make(INTS, "Integer.bitCount(", INTS, ")"));
ops.add(Expression.make(INTS, "Integer.compare(", INTS, ", ", INTS, ")"));
ops.add(Expression.make(INTS, "Integer.compareUnsigned(", INTS, ", ", INTS, ")"));
ops.add(Expression.make(INTS, "Integer.compress(", INTS, ", ", INTS, ")"));
ops.add(Expression.make(INTS, "Integer.divideUnsigned(", INTS, ", ", INTS, ")", withArithmeticException));
ops.add(Expression.make(INTS, "Integer.expand(", INTS, ", ", INTS, ")"));
ops.add(Expression.make(INTS, "Integer.highestOneBit(", INTS, ")"));
ops.add(Expression.make(INTS, "Integer.lowestOneBit(", INTS, ")"));
ops.add(Expression.make(INTS, "Integer.max(", INTS, ", ", INTS, ")"));
ops.add(Expression.make(INTS, "Integer.min(", INTS, ", ", INTS, ")"));
ops.add(Expression.make(INTS, "Integer.numberOfLeadingZeros(", INTS, ")"));
ops.add(Expression.make(INTS, "Integer.numberOfTrailingZeros(", INTS, ")"));
ops.add(Expression.make(INTS, "Integer.remainderUnsigned(", INTS, ", ", INTS, ")", withArithmeticException));
ops.add(Expression.make(INTS, "Integer.reverse(", INTS, ")"));
ops.add(Expression.make(INTS, "Integer.reverseBytes(", INTS, ")"));
ops.add(Expression.make(INTS, "Integer.rotateLeft(", INTS, ", ", INTS, ")"));
ops.add(Expression.make(INTS, "Integer.rotateRight(", INTS, ", ", INTS, ")"));
ops.add(Expression.make(INTS, "Integer.signum(", INTS, ")"));
ops.add(Expression.make(INTS, "Integer.sum(", INTS, ", ", INTS, ")"));
ops.add(Expression.make(LONGS, "Integer.toUnsignedLong(", INTS, ")"));

// ------------ long -------------
// Cast and ternary operator handled above.
// Arithmetic, Bitwise, Relational / Comparison handled above.

// ------------ Long -------------
ops.add(Expression.make(INTS, "Long.bitCount(", LONGS, ")"));
ops.add(Expression.make(INTS, "Long.compare(", LONGS, ", ", LONGS, ")"));
ops.add(Expression.make(INTS, "Long.compareUnsigned(", LONGS, ", ", LONGS, ")"));
ops.add(Expression.make(LONGS, "Long.compress(", LONGS, ", ", LONGS, ")"));
ops.add(Expression.make(LONGS, "Long.divideUnsigned(", LONGS, ", ", LONGS, ")", withArithmeticException));
ops.add(Expression.make(LONGS, "Long.expand(", LONGS, ", ", LONGS, ")"));
ops.add(Expression.make(LONGS, "Long.highestOneBit(", LONGS, ")"));
ops.add(Expression.make(LONGS, "Long.lowestOneBit(", LONGS, ")"));
ops.add(Expression.make(LONGS, "Long.max(", LONGS, ", ", LONGS, ")"));
ops.add(Expression.make(LONGS, "Long.min(", LONGS, ", ", LONGS, ")"));
ops.add(Expression.make(INTS, "Long.numberOfLeadingZeros(", LONGS, ")"));
ops.add(Expression.make(INTS, "Long.numberOfTrailingZeros(", LONGS, ")"));
ops.add(Expression.make(LONGS, "Long.remainderUnsigned(", LONGS, ", ", LONGS, ")", withArithmeticException));
ops.add(Expression.make(LONGS, "Long.reverse(", LONGS, ")"));
ops.add(Expression.make(LONGS, "Long.reverseBytes(", LONGS, ")"));
ops.add(Expression.make(LONGS, "Long.rotateLeft(", LONGS, ", ", INTS, ")"));
ops.add(Expression.make(LONGS, "Long.rotateRight(", LONGS, ", ", INTS, ")"));
ops.add(Expression.make(INTS, "Long.signum(", LONGS, ")"));
ops.add(Expression.make(LONGS, "Long.sum(", LONGS, ", ", LONGS, ")"));

// ------------ float -------------
// Cast and ternary operator handled above.
// Arithmetic and Relational / Comparison handled above.

// ------------ Float -------------
ops.add(Expression.make(INTS, "Float.compare(", FLOATS, ", ", FLOATS, ")"));
ops.add(Expression.make(INTS, "Float.floatToIntBits(", FLOATS, ")"));
ops.add(Expression.make(INTS, "Float.floatToRawIntBits(", FLOATS, ")", withNondeterministicResult));
// Note: there are multiple NaN values with different bit representations.
ops.add(Expression.make(FLOATS, "Float.float16ToFloat(", SHORTS, ")"));
ops.add(Expression.make(FLOATS, "Float.intBitsToFloat(", INTS, ")"));
ops.add(Expression.make(BOOLEANS, "Float.isFinite(", FLOATS, ")"));
ops.add(Expression.make(BOOLEANS, "Float.isInfinite(", FLOATS, ")"));
ops.add(Expression.make(BOOLEANS, "Float.isNaN(", FLOATS, ")"));
ops.add(Expression.make(FLOATS, "Float.max(", FLOATS, ", ", FLOATS, ")"));
ops.add(Expression.make(FLOATS, "Float.min(", FLOATS, ", ", FLOATS, ")"));
ops.add(Expression.make(FLOATS, "Float.sum(", FLOATS, ", ", FLOATS, ")"));

// ------------ double -------------
// Cast and ternary operator handled above.
// Arithmetic and Relational / Comparison handled above.

// ------------ Double -------------
ops.add(Expression.make(INTS, "Double.compare(", DOUBLES, ", ", DOUBLES, ")"));
ops.add(Expression.make(LONGS, "Double.doubleToLongBits(", DOUBLES, ")"));
// Note: there are multiple NaN values with different bit representations.
ops.add(Expression.make(LONGS, "Double.doubleToRawLongBits(", DOUBLES, ")", withNondeterministicResult));
ops.add(Expression.make(DOUBLES, "Double.longBitsToDouble(", LONGS, ")"));
ops.add(Expression.make(BOOLEANS, "Double.isFinite(", DOUBLES, ")"));
ops.add(Expression.make(BOOLEANS, "Double.isInfinite(", DOUBLES, ")"));
ops.add(Expression.make(BOOLEANS, "Double.isNaN(", DOUBLES, ")"));
ops.add(Expression.make(DOUBLES, "Double.max(", DOUBLES, ", ", DOUBLES, ")"));
ops.add(Expression.make(DOUBLES, "Double.min(", DOUBLES, ", ", DOUBLES, ")"));
ops.add(Expression.make(DOUBLES, "Double.sum(", DOUBLES, ", ", DOUBLES, ")"));

// ------------ boolean -------------
// Cast and ternary operator handled above.
// There are no boolean arithmetic operators

// Logical operators
ops.add(Expression.make(BOOLEANS, "(!(", BOOLEANS, "))"));
ops.add(Expression.make(BOOLEANS, "(", BOOLEANS, " || ", BOOLEANS, ")"));
ops.add(Expression.make(BOOLEANS, "(", BOOLEANS, " && ", BOOLEANS, ")"));
ops.add(Expression.make(BOOLEANS, "(", BOOLEANS, " ^ ", BOOLEANS, ")"));

// ------------ Boolean -------------
ops.add(Expression.make(INTS, "Boolean.compare(", BOOLEANS, ", ", BOOLEANS, ")"));
ops.add(Expression.make(BOOLEANS, "Boolean.logicalAnd(", BOOLEANS, ", ", BOOLEANS, ")"));
ops.add(Expression.make(BOOLEANS, "Boolean.logicalOr(", BOOLEANS, ", ", BOOLEANS, ")"));
ops.add(Expression.make(BOOLEANS, "Boolean.logicalXor(", BOOLEANS, ", ", BOOLEANS, ")"));

// TODO: Math and other classes.

// Make sure the list is not modifiable.
return List.copyOf(ops);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
import compiler.lib.generators.RestrictableGenerator;

import compiler.lib.template_framework.DataName;
import compiler.lib.template_framework.Template;
import compiler.lib.template_framework.TemplateToken;
import static compiler.lib.template_framework.Template.body;

/**
* The {@link PrimitiveType} models Java's primitive types, and provides a set
Expand Down Expand Up @@ -148,4 +151,91 @@ public boolean isFloating() {
case FLOAT, DOUBLE -> true;
};
}

/**
* Calls the corresponding pseudo random number generator from
* {@link #generateLibraryRNG}, for the given type. Accordingly,
* one must generate {@link #generateLibraryRNG} into the same
* test if one wants to use this method.
*
* Note: if you simply need a compile time constant, then please
* use {@link #con} instead.
*
* @return the token representing the method call to obtain a
* random value for the given type at runtime.
*/
public Object callLibraryRNG() {
return switch (kind) {
case BYTE -> "LibraryRNG.nextByte()";
case SHORT -> "LibraryRNG.nextShort()";
case CHAR -> "LibraryRNG.nextChar()";
case INT -> "LibraryRNG.nextInt()";
case LONG -> "LibraryRNG.nextLong()";
case FLOAT -> "LibraryRNG.nextFloat()";
case DOUBLE -> "LibraryRNG.nextDouble()";
case BOOLEAN -> "LibraryRNG.nextBoolean()";
};
}

/**
* Generates the {@code LibraryRNG} class, which makes a set of pseudo
* random number generators available, wrapping {@link Generators}. This
* is supposed to be used in tandem with {@link #callLibraryRNG}.
*
* Note: you must ensure that all required imports are performed:
* {@code java.util.Random}
* {@code jdk.test.lib.Utils}
* {@code compiler.lib.generators.*}
*
* @return a TemplateToken that holds all the {@code LibraryRNG} class.
*/
public static TemplateToken generateLibraryRNG() {
var template = Template.make(() -> body(
"""
public static class LibraryRNG {
private static final Random RANDOM = Utils.getRandomInstance();
private static final RestrictableGenerator<Integer> GEN_BYTE = Generators.G.safeRestrict(Generators.G.ints(), Byte.MIN_VALUE, Byte.MAX_VALUE);
private static final RestrictableGenerator<Integer> GEN_CHAR = Generators.G.safeRestrict(Generators.G.ints(), Character.MIN_VALUE, Character.MAX_VALUE);
private static final RestrictableGenerator<Integer> GEN_SHORT = Generators.G.safeRestrict(Generators.G.ints(), Short.MIN_VALUE, Short.MAX_VALUE);
private static final RestrictableGenerator<Integer> GEN_INT = Generators.G.ints();
private static final RestrictableGenerator<Long> GEN_LONG = Generators.G.longs();
private static final Generator<Double> GEN_DOUBLE = Generators.G.doubles();
private static final Generator<Float> GEN_FLOAT = Generators.G.floats();

public static byte nextByte() {
return GEN_BYTE.next().byteValue();
}

public static short nextShort() {
return GEN_SHORT.next().shortValue();
}

public static char nextChar() {
return (char)GEN_CHAR.next().intValue();
}

public static int nextInt() {
return GEN_INT.next();
}

public static long nextLong() {
return GEN_LONG.next();
}

public static float nextFloat() {
return GEN_FLOAT.next();
}

public static double nextDouble() {
return GEN_DOUBLE.next();
}

public static boolean nextBoolean() {
return RANDOM.nextBoolean();
}
}
"""
));
return template.asToken();
};
}
Loading