From 8d564da90e76ac47830a324f8d090056bf500fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Wed, 13 Aug 2025 16:16:16 +0200 Subject: [PATCH 01/13] Add TestFramework::addCrossProductScenarios --- .../jtreg/compiler/lib/ir_framework/README.md | 2 +- .../lib/ir_framework/TestFramework.java | 39 ++++++++ .../tests/TestScenariosCrossProduct.java | 93 +++++++++++++++++++ 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/README.md b/test/hotspot/jtreg/compiler/lib/ir_framework/README.md index 6bdae26259908..2a454f2990d5b 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/README.md +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/README.md @@ -146,7 +146,7 @@ More information about IR matching can be found in the Javadocs of [IR](./IR.jav ### 2.3 Test VM Flags and Scenarios The recommended way to use the framework is by defining a single `@run driver` statement in the JTreg header which, however, does not allow the specification of additional test VM flags. Instead, the user has the possibility to provide VM flags by calling `TestFramework.runWithFlags()` or by creating a `TestFramework` builder object on which `addFlags()` can be called. -If a user wants to provide multiple flag combinations for a single test, he or she has the option to provide different scenarios. A scenario based flag will always have precedence over other user defined flags. More information about scenarios can be found in the Javadocs of [Scenario](./Scenario.java). +If a user wants to provide multiple flag combinations for a single test, he or she has the option to provide different scenarios. A scenario based flag will always have precedence over other user defined flags. More information about scenarios can be found in the Javadocs of [Scenario](./Scenario.java). If a user wants to test all combinations of multiple sets of flags, they can use `TestFramework.addCrossProductScenarios()`. ### 2.4 Compiler Controls The framework allows the use of additional compiler control annotations for helper method and classes in the same fashion as JMH does. The following annotations are supported and described in the referenced Javadocs for the annotation class: diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java index 6782bd68fed04..67e80bbc1a57b 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java @@ -45,7 +45,9 @@ import java.net.URLClassLoader; import java.nio.file.Path; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * This class represents the main entry point to the test framework whose main purpose is to perform regex-based checks on @@ -332,6 +334,43 @@ public TestFramework addScenarios(Scenario... scenarios) { return this; } + /** + * Add the cross-product (cartesian product) of sets of flags as Scenarios. + * + * @param sets sets of flags to generate the cross product for. + * @return the same framework instance. + */ + @SafeVarargs + final public TestFramework addCrossProductScenarios(Set... flagSets) { + TestFormat.checkAndReport(flagSets != null && Arrays.stream(flagSets).noneMatch(Objects::isNull), + "Flags must not be null"); + int initIdx = 0; + if (this.scenarioIndices != null && !this.scenarioIndices.isEmpty()) { + initIdx = this.scenarioIndices.stream().max(Comparator.comparingInt(Integer::intValue)).get() + 1; + } + AtomicInteger idx = new AtomicInteger(initIdx); + + return addScenarios( + crossProductHelper(0, flagSets) + .map(flags -> new Scenario(idx.getAndIncrement(), flags.toArray(new String[0]))) + .collect(Collectors.toList()).toArray(new Scenario[0])); + } + + @SafeVarargs + private static Stream> crossProductHelper(int idx, Set... sets) { + if (idx == sets.length) { + Set empty = Set.of(); + return Set.of(empty).stream(); + } + return sets[idx].stream() + .flatMap(setElement -> crossProductHelper(idx + 1, sets) + .map(set -> { + Set newSet = new HashSet(set); + newSet.add(setElement); + return newSet; + })); + } + /** * Add test classes to boot classpath. This adds all classes found on path {@link jdk.test.lib.Utils#TEST_CLASSES} * to the boot classpath with "-Xbootclasspath/a". This is useful when trying to run tests in a privileged mode. diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java new file mode 100644 index 0000000000000..0972d91782925 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java @@ -0,0 +1,93 @@ +/* + * 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 ir_framework.tests; + +import java.util.Set; + +import compiler.lib.ir_framework.*; +import compiler.lib.ir_framework.shared.TestRunException; +import jdk.test.lib.Asserts; + +/* + * @test + * @requires vm.debug == true & vm.compMode != "Xint" & vm.compiler2.enabled & vm.flagless + * @summary Test cross product scenarios with the framework. + * @library /test/lib /testlibrary_tests / + * @run driver ir_framework.tests.TestScenariosCrossProduct + */ + +public class TestScenariosCrossProduct { + public static void main(String[] args) { + // Single set should test all flags in the set by themselves. + try { + TestFramework t1 = new TestFramework(); + t1.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=51", + "-XX:TLABRefillWasteFraction=53", + "-XX:TLABRefillWasteFraction=64")); + t1.start(); + Asserts.fail("Should have thrown exception"); + } catch (TestRunException e) { + if (!e.getMessage().contains("The following scenarios have failed: #0, #1, #2")) { + throw e; + } + } + + // The cross product of a set with one element and a set with three elements is three sets. + try { + TestFramework t2 = new TestFramework(); + t2.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=53"), + Set.of("-XX:+UseNewCode", "-XX:+UseNewCode2", "-XX:+UseNewCode3")); + t2.start(); + Asserts.fail("Should have thrown exception"); + } catch (TestRunException e) { + if (!e.getMessage().contains("The following scenarios have failed: #0, #1, #2")) { + throw e; + } + } + + // The cross product of two sets with two elements is four sets. + try { + TestFramework t3 = new TestFramework(); + t3.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=53", "-XX:TLABRefillWasteFraction=64"), + Set.of("-XX:+UseNewCode", "-XX:-UseNewCode")); + t3.start(); + Asserts.fail("Should have thrown exception"); + } catch (TestRunException e) { + if (!e.getMessage().contains("The following scenarios have failed: #0, #1, #2, #3")) { + throw e; + } + } + } + + @Test + @IR(applyIf = {"TLABRefillWasteFraction", "64"}, counts = {IRNode.CALL, "1"}) + public void fail1() { + } + + @Test + @IR(applyIf = {"TLABRefillWasteFraction", "51"}, counts = {IRNode.CALL, "1"}) + @IR(applyIf = {"TLABRefillWasteFraction", "53"}, counts = {IRNode.CALL, "1"}) + public void fail2() { + } +} From 57b3afed1ec7137bdbc571c13f2bc340afd645cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Wed, 13 Aug 2025 16:28:00 +0200 Subject: [PATCH 02/13] Convert test to cross product scenarios --- .../TestFloatConversionsVector.java | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/test/hotspot/jtreg/compiler/vectorization/TestFloatConversionsVector.java b/test/hotspot/jtreg/compiler/vectorization/TestFloatConversionsVector.java index 482dcf934c59b..3ba9fe7472c4d 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestFloatConversionsVector.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestFloatConversionsVector.java @@ -27,14 +27,13 @@ * @summary Auto-vectorize Float.floatToFloat16, Float.float16ToFloat APIs * @requires vm.compiler2.enabled * @library /test/lib / - * @run driver compiler.vectorization.TestFloatConversionsVector nCOH_nAV - * @run driver compiler.vectorization.TestFloatConversionsVector nCOH_yAV - * @run driver compiler.vectorization.TestFloatConversionsVector yCOH_nAV - * @run driver compiler.vectorization.TestFloatConversionsVector yCOH_yAV + * @run driver compiler.vectorization.TestFloatConversionsVector */ package compiler.vectorization; +import java.util.Set; + import compiler.lib.ir_framework.*; import jdk.test.lib.Asserts; @@ -49,13 +48,8 @@ public class TestFloatConversionsVector { public static void main(String args[]) { TestFramework framework = new TestFramework(TestFloatConversionsVector.class); framework.addFlags("-XX:-TieredCompilation", "-XX:CompileThresholdScaling=0.3"); - switch (args[0]) { - case "nCOH_nAV" -> { framework.addFlags("-XX:-UseCompactObjectHeaders", "-XX:-AlignVector"); } - case "nCOH_yAV" -> { framework.addFlags("-XX:-UseCompactObjectHeaders", "-XX:+AlignVector"); } - case "yCOH_nAV" -> { framework.addFlags("-XX:+UseCompactObjectHeaders", "-XX:-AlignVector"); } - case "yCOH_yAV" -> { framework.addFlags("-XX:+UseCompactObjectHeaders", "-XX:+AlignVector"); } - default -> { throw new RuntimeException("Test argument not recognized: " + args[0]); } - }; + framework.addCrossProductScenarios(Set.of("-XX:-UseCompactObjectHeaders", "-XX:+UseCompactObjectHeaders"), + Set.of("-XX:-AlignVector", "-XX:+AlignVector")); framework.start(); System.out.println("PASSED"); } From 0bd8c6a76301d318df49548854d7304cc972afaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Mon, 18 Aug 2025 10:09:00 +0200 Subject: [PATCH 03/13] =?UTF-8?q?Apply=20Beno=C3=AEt's=20suggestion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: BenoƮt Maillard --- test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java index 67e80bbc1a57b..fa8213f48125b 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java @@ -365,7 +365,7 @@ private static Stream> crossProductHelper(int idx, Set... se return sets[idx].stream() .flatMap(setElement -> crossProductHelper(idx + 1, sets) .map(set -> { - Set newSet = new HashSet(set); + Set newSet = new HashSet<>(set); newSet.add(setElement); return newSet; })); From ed8a9beb089f4d819ebfda351ef63d701e508735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Wed, 20 Aug 2025 18:55:14 +0200 Subject: [PATCH 04/13] Better testing --- .../tests/TestScenariosCrossProduct.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java index 0972d91782925..4bc71f779ad12 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java @@ -61,7 +61,8 @@ public static void main(String[] args) { t2.start(); Asserts.fail("Should have thrown exception"); } catch (TestRunException e) { - if (!e.getMessage().contains("The following scenarios have failed: #0, #1, #2")) { + if (!e.getMessage().contains("The following scenarios have failed: #0, #1, #2")|| + e.getMessage().contains("The following scenarios have failed: #0, #1, #2, #3")) { throw e; } } @@ -74,7 +75,20 @@ public static void main(String[] args) { t3.start(); Asserts.fail("Should have thrown exception"); } catch (TestRunException e) { - if (!e.getMessage().contains("The following scenarios have failed: #0, #1, #2, #3")) { + if (!e.getMessage().contains("The following scenarios have failed: #0, #1, #2, #3") || + e.getMessage().contains("The following scenarios have failed: #0, #1, #2, #3, #4")) { + throw e; + } + } + + try { + TestFramework t4 = new TestFramework(); + t4.addCrossProductScenarios(Set.of("-XX:TLABRefillWastFraction=50 -XX:+UseNewCode", "-XX:TLABRefillWasteFraction=40"), + Set.of("-XX:+UseNewCode2")); + } catch (TestRunException e) { + if (!e.getMessage().contains("The following scenarios have failed: #0") || + !e.getMessage().contains("The following scenarios have failed: #1") || + e.getMessage().contains("The following scenarios have failed: #0, #1")) { throw e; } } @@ -90,4 +104,9 @@ public void fail1() { @IR(applyIf = {"TLABRefillWasteFraction", "53"}, counts = {IRNode.CALL, "1"}) public void fail2() { } + + @Test + @IR(applyIfAnd = {"TLABRefillWasteFraction", "50", "UseNewCode", "true"}, counts = {IRNode.CALL, "1"}) + public void failPair() { + } } From f59e9d9d1126c7b5efa5e613c9225fb95aae2c30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Wed, 20 Aug 2025 19:01:13 +0200 Subject: [PATCH 05/13] Make the test work --- .../ir_framework/tests/TestScenariosCrossProduct.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java index 4bc71f779ad12..3e3c054314bbe 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java @@ -85,9 +85,11 @@ public static void main(String[] args) { TestFramework t4 = new TestFramework(); t4.addCrossProductScenarios(Set.of("-XX:TLABRefillWastFraction=50 -XX:+UseNewCode", "-XX:TLABRefillWasteFraction=40"), Set.of("-XX:+UseNewCode2")); + t4.start(); + Asserts.fail("Should have thrown exception"); } catch (TestRunException e) { - if (!e.getMessage().contains("The following scenarios have failed: #0") || - !e.getMessage().contains("The following scenarios have failed: #1") || + if (!(e.getMessage().contains("The following scenarios have failed: #0") || + e.getMessage().contains("The following scenarios have failed: #1")) || e.getMessage().contains("The following scenarios have failed: #0, #1")) { throw e; } From 328a00df89644f5a4d4b9015cf05af2364264a31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Thu, 21 Aug 2025 18:04:24 +0200 Subject: [PATCH 06/13] post processing of flags and documentation --- .../lib/ir_framework/TestFramework.java | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java index fa8213f48125b..bf46180c623d4 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java @@ -335,7 +335,24 @@ public TestFramework addScenarios(Scenario... scenarios) { } /** - * Add the cross-product (cartesian product) of sets of flags as Scenarios. + * Add the cross-product (cartesian product) of sets of flags as Scenarios. Unlike when when constructing + * scenarios directly a string can contain multiple flags separated with a space. This allows grouping + * flags that have to be specified togeher. Further, an empty string in a set stands in for "no flag". + *

+ * Example: + *

+     *     addCrossProductScenarios(Set.of("", "-Xint", "-Xbatch -XX:-TieredCompilation"),
+     *                              Set.of("-XX:+UseNewCode", "-XX:UseNewCode2"))
+     * 
+ * produces the following Scenarios + *
+     *     Scenario(0, "-XX:+UseNewCode")
+     *     Scenario(1, "-XX:+UseNewCode2")
+     *     Scenario(2, "-Xint", "-XX:+UseNewCode")
+     *     Scenario(3, "-Xint", "-XX:+UseNewCode2")
+     *     Scenario(4, "-Xbatch -XX:-TieredCompilation", "-XX:+UseNewCode")
+     *     Scenario(5, "-Xbatch -XX:-TieredCompilation", "-XX:+UseNewCode2")
+     * 
* * @param sets sets of flags to generate the cross product for. * @return the same framework instance. @@ -343,17 +360,24 @@ public TestFramework addScenarios(Scenario... scenarios) { @SafeVarargs final public TestFramework addCrossProductScenarios(Set... flagSets) { TestFormat.checkAndReport(flagSets != null && Arrays.stream(flagSets).noneMatch(Objects::isNull), - "Flags must not be null"); + "Flags must not be null"); int initIdx = 0; if (this.scenarioIndices != null && !this.scenarioIndices.isEmpty()) { initIdx = this.scenarioIndices.stream().max(Comparator.comparingInt(Integer::intValue)).get() + 1; } AtomicInteger idx = new AtomicInteger(initIdx); - return addScenarios( - crossProductHelper(0, flagSets) - .map(flags -> new Scenario(idx.getAndIncrement(), flags.toArray(new String[0]))) - .collect(Collectors.toList()).toArray(new Scenario[0])); + Scenario[] newScenarios = crossProductHelper(0, flagSets) + .map(flags -> new Scenario( + idx.getAndIncrement(), + flags.stream() // Process flags + .filter(s -> !s.isEmpty()) // Remove empty flags + .map(s -> Set.of(s.split("[ ]"))) // Split muliple flags in the same string into separate strings + .flatMap(Collection::stream) + .collect(Collectors.toList()) + .toArray(new String[0]))) + .collect(Collectors.toList()).toArray(new Scenario[0]); + return addScenarios(newScenarios); } @SafeVarargs From 44556e59e2457f6c4442d7fb47bc96fec149539b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Thu, 21 Aug 2025 18:04:55 +0200 Subject: [PATCH 07/13] Better counting in tests --- .../tests/TestScenariosCrossProduct.java | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java index 3e3c054314bbe..124dbc0c24cf0 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java @@ -38,6 +38,12 @@ */ public class TestScenariosCrossProduct { + static void hasNFailures(String s, int count) { + if (s.matches("The following scenarios have failed: (#[0-9](?:, )?){" + count + "}\\.")) { + throw new RuntimeException("Expected " + count + " failures in \"" + s + "\""); + } + } + public static void main(String[] args) { // Single set should test all flags in the set by themselves. try { @@ -48,9 +54,7 @@ public static void main(String[] args) { t1.start(); Asserts.fail("Should have thrown exception"); } catch (TestRunException e) { - if (!e.getMessage().contains("The following scenarios have failed: #0, #1, #2")) { - throw e; - } + hasNFailures(e.getMessage(), 3); } // The cross product of a set with one element and a set with three elements is three sets. @@ -61,10 +65,7 @@ public static void main(String[] args) { t2.start(); Asserts.fail("Should have thrown exception"); } catch (TestRunException e) { - if (!e.getMessage().contains("The following scenarios have failed: #0, #1, #2")|| - e.getMessage().contains("The following scenarios have failed: #0, #1, #2, #3")) { - throw e; - } + hasNFailures(e.getMessage(), 3); } // The cross product of two sets with two elements is four sets. @@ -75,24 +76,28 @@ public static void main(String[] args) { t3.start(); Asserts.fail("Should have thrown exception"); } catch (TestRunException e) { - if (!e.getMessage().contains("The following scenarios have failed: #0, #1, #2, #3") || - e.getMessage().contains("The following scenarios have failed: #0, #1, #2, #3, #4")) { - throw e; - } + hasNFailures(e.getMessage(), 4); } try { TestFramework t4 = new TestFramework(); - t4.addCrossProductScenarios(Set.of("-XX:TLABRefillWastFraction=50 -XX:+UseNewCode", "-XX:TLABRefillWasteFraction=40"), + t4.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=50 -XX:+UseNewCode", "-XX:TLABRefillWasteFraction=40"), Set.of("-XX:+UseNewCode2")); t4.start(); Asserts.fail("Should have thrown exception"); } catch (TestRunException e) { - if (!(e.getMessage().contains("The following scenarios have failed: #0") || - e.getMessage().contains("The following scenarios have failed: #1")) || - e.getMessage().contains("The following scenarios have failed: #0, #1")) { - throw e; - } + hasNFailures(e.getMessage(), 1); + } + + // Test with an empty string. Only 4 scenarios fail. + try { + TestFramework t5 = new TestFramework(); + t5.addCrossProductScenarios(Set.of("", "-XX:TLABRefillWasteFraction=51", "-XX:TLABRefillWasteFraction=64"), + Set.of("-XX:+UseNewCode", "-XX:+UseNewCode2")); + t5.start(); + Asserts.fail("Should have thrown exception"); + } catch (TestRunException e) { + hasNFailures(e.getMessage(), 4); } } From 273e5f64db4992d2c9e31ab5f2c58e2504a332f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Thu, 21 Aug 2025 18:26:00 +0200 Subject: [PATCH 08/13] Fix test --- .../tests/TestScenariosCrossProduct.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java index 124dbc0c24cf0..b625f99cb557f 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java @@ -39,7 +39,7 @@ public class TestScenariosCrossProduct { static void hasNFailures(String s, int count) { - if (s.matches("The following scenarios have failed: (#[0-9](?:, )?){" + count + "}\\.")) { + if (!s.matches("The following scenarios have failed: (#[0-9](, )?){" + count + "}. Please check stderr for more information.")) { throw new RuntimeException("Expected " + count + " failures in \"" + s + "\""); } } @@ -89,25 +89,29 @@ public static void main(String[] args) { hasNFailures(e.getMessage(), 1); } - // Test with an empty string. Only 4 scenarios fail. + // Test with an empty string. All 6 scenarios fail because 64 is the default value for TLABRefillWasteFraction. try { TestFramework t5 = new TestFramework(); - t5.addCrossProductScenarios(Set.of("", "-XX:TLABRefillWasteFraction=51", "-XX:TLABRefillWasteFraction=64"), + t5.addCrossProductScenarios(Set.of("", "-XX:TLABRefillWasteFraction=51", "-XX:TLABRefillWasteFraction=53"), Set.of("-XX:+UseNewCode", "-XX:+UseNewCode2")); t5.start(); Asserts.fail("Should have thrown exception"); } catch (TestRunException e) { - hasNFailures(e.getMessage(), 4); + hasNFailures(e.getMessage(), 6); } } @Test @IR(applyIf = {"TLABRefillWasteFraction", "64"}, counts = {IRNode.CALL, "1"}) - public void fail1() { + public void failDefault() { } @Test @IR(applyIf = {"TLABRefillWasteFraction", "51"}, counts = {IRNode.CALL, "1"}) + public void fail1() { + } + + @Test @IR(applyIf = {"TLABRefillWasteFraction", "53"}, counts = {IRNode.CALL, "1"}) public void fail2() { } From 7bab7759b4cc9900cea166d0575548c1eb7a580a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Fri, 22 Aug 2025 11:31:53 +0200 Subject: [PATCH 09/13] Improvements prompted by Emanuel --- .../lib/ir_framework/TestFramework.java | 51 ++++++++++--------- .../tests/TestScenariosCrossProduct.java | 42 +++++++++++++++ 2 files changed, 70 insertions(+), 23 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java index bf46180c623d4..c55d22ed04586 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java @@ -359,42 +359,47 @@ public TestFramework addScenarios(Scenario... scenarios) { */ @SafeVarargs final public TestFramework addCrossProductScenarios(Set... flagSets) { - TestFormat.checkAndReport(flagSets != null && Arrays.stream(flagSets).noneMatch(Objects::isNull), - "Flags must not be null"); + TestFormat.checkAndReport(flagSets != null && + Arrays.stream(flagSets).noneMatch(Objects::isNull) && + Arrays.stream(flagSets).flatMap(Set::stream).noneMatch(Objects::isNull), + "Flags must not be null"); + if (flagSets.length == 0) { + return this; + } + int initIdx = 0; if (this.scenarioIndices != null && !this.scenarioIndices.isEmpty()) { initIdx = this.scenarioIndices.stream().max(Comparator.comparingInt(Integer::intValue)).get() + 1; } AtomicInteger idx = new AtomicInteger(initIdx); - Scenario[] newScenarios = crossProductHelper(0, flagSets) + Stream> crossProduct = Arrays.stream(flagSets) + .reduce( + Stream.of(Collections.emptyList()), + (acc, set) -> + acc.flatMap(list -> + set.stream().map(element -> { + List newList = new ArrayList<>(list); + newList.add(element); + return newList; + }) + ), + (a, b) -> Stream.concat(a, b)); + + Scenario[] newScenarios = crossProduct .map(flags -> new Scenario( idx.getAndIncrement(), flags.stream() // Process flags - .filter(s -> !s.isEmpty()) // Remove empty flags - .map(s -> Set.of(s.split("[ ]"))) // Split muliple flags in the same string into separate strings - .flatMap(Collection::stream) - .collect(Collectors.toList()) - .toArray(new String[0]))) + .map(s -> Set.of(s.split("[ ]"))) // Split muliple flags in the same string into separate strings + .flatMap(Collection::stream) + .filter(s -> !s.isEmpty()) // Remove empty string flags + .distinct() + .collect(Collectors.toList()) + .toArray(new String[0]))) .collect(Collectors.toList()).toArray(new Scenario[0]); return addScenarios(newScenarios); } - @SafeVarargs - private static Stream> crossProductHelper(int idx, Set... sets) { - if (idx == sets.length) { - Set empty = Set.of(); - return Set.of(empty).stream(); - } - return sets[idx].stream() - .flatMap(setElement -> crossProductHelper(idx + 1, sets) - .map(set -> { - Set newSet = new HashSet<>(set); - newSet.add(setElement); - return newSet; - })); - } - /** * Add test classes to boot classpath. This adds all classes found on path {@link jdk.test.lib.Utils#TEST_CLASSES} * to the boot classpath with "-Xbootclasspath/a". This is useful when trying to run tests in a privileged mode. diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java index b625f99cb557f..07517d59bef00 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java @@ -27,6 +27,7 @@ import compiler.lib.ir_framework.*; import compiler.lib.ir_framework.shared.TestRunException; +import compiler.lib.ir_framework.shared.TestFormatException; import jdk.test.lib.Asserts; /* @@ -45,6 +46,30 @@ static void hasNFailures(String s, int count) { } public static void main(String[] args) { + // Test argument handling + try { + TestFramework t = new TestFramework(); + t.addCrossProductScenarios(null); + Asserts.fail("Should not have thrown exception"); + } catch (TestFormatException e) {} + try { + TestFramework t = new TestFramework(); + t.addCrossProductScenarios(Set.of("foo", "bar"), null); + Asserts.fail("Should not have thrown exception"); + } catch (TestFormatException e) {} + try { + TestFramework t = new TestFramework(); + t.addCrossProductScenarios(Set.of("blub"), Set.of("foo", null)); + Asserts.fail("Should not have thrown exception"); + } catch (NullPointerException e) {} // Set.of prevents null elements + try { + TestFramework t = new TestFramework(); + t.addCrossProductScenarios(); + } catch (TestFormatException e) { + Asserts.fail("Should not have thrown exception"); + } + + // Single set should test all flags in the set by themselves. try { TestFramework t1 = new TestFramework(); @@ -79,6 +104,7 @@ public static void main(String[] args) { hasNFailures(e.getMessage(), 4); } + // Test with a pair of flags. try { TestFramework t4 = new TestFramework(); t4.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=50 -XX:+UseNewCode", "-XX:TLABRefillWasteFraction=40"), @@ -99,6 +125,22 @@ public static void main(String[] args) { } catch (TestRunException e) { hasNFailures(e.getMessage(), 6); } + + try { + TestFramework t6 = new TestFramework(); + t6.addScenarios(new Scenario(0, "-XX:TLABRefillWasteFraction=50", "-XX:+UseNewCode")); // failPair + t6.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=51", "-XX:TLABRefillWasteFraction=53"), + Set.of("-XX:+UseNewCode", "-XX:+UseNewCode2")); + try { + t6.addScenarios(new Scenario(4, "-XX:+UseNewCode3")); // fails because index 4 is already used + Asserts.fail("Should have thrown exception"); + } catch (TestFormatException e) {} + t6.addScenarios(new Scenario(5, "-XX:+UseNewCode3")); // fail default + t6.start(); + Asserts.fail("Should have thrown exception"); + } catch (TestRunException e) { + hasNFailures(e.getMessage(), 6); + } } @Test From 97242a0a1f29bd877f53989a6ae4937bbf9d6fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Fri, 22 Aug 2025 19:19:29 +0200 Subject: [PATCH 10/13] Fix copy pasta mistakes --- .../ir_framework/tests/TestScenariosCrossProduct.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java index 07517d59bef00..8535270a06a59 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java @@ -50,17 +50,17 @@ public static void main(String[] args) { try { TestFramework t = new TestFramework(); t.addCrossProductScenarios(null); - Asserts.fail("Should not have thrown exception"); + Asserts.fail("Should have thrown exception"); } catch (TestFormatException e) {} try { TestFramework t = new TestFramework(); t.addCrossProductScenarios(Set.of("foo", "bar"), null); - Asserts.fail("Should not have thrown exception"); + Asserts.fail("Should have thrown exception"); } catch (TestFormatException e) {} try { TestFramework t = new TestFramework(); t.addCrossProductScenarios(Set.of("blub"), Set.of("foo", null)); - Asserts.fail("Should not have thrown exception"); + Asserts.fail("Should have thrown exception"); } catch (NullPointerException e) {} // Set.of prevents null elements try { TestFramework t = new TestFramework(); From 4263d61142a20d422bf1e77cd695dbb7dd50bc48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Fri, 22 Aug 2025 19:55:40 +0200 Subject: [PATCH 11/13] Improve comments --- .../lib/ir_framework/TestFramework.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java index c55d22ed04586..6c94e8b3c352c 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java @@ -375,24 +375,24 @@ final public TestFramework addCrossProductScenarios(Set... flagSets) { Stream> crossProduct = Arrays.stream(flagSets) .reduce( - Stream.of(Collections.emptyList()), - (acc, set) -> - acc.flatMap(list -> - set.stream().map(element -> { - List newList = new ArrayList<>(list); - newList.add(element); + Stream.of(Collections.emptyList()), // Initialize Stream> acc with a Stream containing an empty list of Strings. + (acc, set) -> // (Stream>, Stream>) -> Stream> + acc.flatMap(lAcc -> // For each List> lAcc in acc... + set.stream().map(flag -> { // ...and each flag in the current set... + List newList = new ArrayList<>(lAcc); // ...create a new list containing lAcc... + newList.add(flag); // ...and append the flag. return newList; - }) - ), - (a, b) -> Stream.concat(a, b)); + }) // This results in one List> for each lAcc... + ), // ...that get flattend into one big List>. + (a, b) -> Stream.concat(a, b)); // combiner; if any reduction steps are executed in parallel, just concat two streams. Scenario[] newScenarios = crossProduct - .map(flags -> new Scenario( + .map(flags -> new Scenario( // For each List flags in crossProduct create a new Scenario. idx.getAndIncrement(), flags.stream() // Process flags - .map(s -> Set.of(s.split("[ ]"))) // Split muliple flags in the same string into separate strings - .flatMap(Collection::stream) - .filter(s -> !s.isEmpty()) // Remove empty string flags + .map(s -> Set.of(s.split("[ ]"))) // Split muliple flags in the same string into separate strings. + .flatMap(Collection::stream) // Flatten the Stream> into Stream>. + .filter(s -> !s.isEmpty()) // Remove empty string flags. .distinct() .collect(Collectors.toList()) .toArray(new String[0]))) From f5723e0e6e1428c2596d4bfb6ef55940914f6697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Fri, 22 Aug 2025 19:57:04 +0200 Subject: [PATCH 12/13] Fix indentation --- .../lib/ir_framework/TestFramework.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java index 6c94e8b3c352c..591e45407640b 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java @@ -387,16 +387,16 @@ final public TestFramework addCrossProductScenarios(Set... flagSets) { (a, b) -> Stream.concat(a, b)); // combiner; if any reduction steps are executed in parallel, just concat two streams. Scenario[] newScenarios = crossProduct - .map(flags -> new Scenario( // For each List flags in crossProduct create a new Scenario. - idx.getAndIncrement(), - flags.stream() // Process flags - .map(s -> Set.of(s.split("[ ]"))) // Split muliple flags in the same string into separate strings. - .flatMap(Collection::stream) // Flatten the Stream> into Stream>. - .filter(s -> !s.isEmpty()) // Remove empty string flags. - .distinct() - .collect(Collectors.toList()) - .toArray(new String[0]))) - .collect(Collectors.toList()).toArray(new Scenario[0]); + .map(flags -> new Scenario( // For each List flags in crossProduct create a new Scenario. + idx.getAndIncrement(), + flags.stream() // Process flags + .map(s -> Set.of(s.split("[ ]"))) // Split muliple flags in the same string into separate strings. + .flatMap(Collection::stream) // Flatten the Stream> into Stream>. + .filter(s -> !s.isEmpty()) // Remove empty string flags. + .distinct() + .collect(Collectors.toList()) + .toArray(new String[0]))) + .collect(Collectors.toList()).toArray(new Scenario[0]); return addScenarios(newScenarios); } From 2e36929f7cbd4958991133240afd140941d03372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Fri, 22 Aug 2025 19:59:23 +0200 Subject: [PATCH 13/13] Remove excess newline --- .../ir_framework/tests/TestScenariosCrossProduct.java | 1 - 1 file changed, 1 deletion(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java index 8535270a06a59..9562202352e6c 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java @@ -69,7 +69,6 @@ public static void main(String[] args) { Asserts.fail("Should not have thrown exception"); } - // Single set should test all flags in the set by themselves. try { TestFramework t1 = new TestFramework();