/* * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ /* * @test id=Xbatch * @bug 8288981 8350579 * @summary Test all possible cases in which Assertion Predicates are required such that the graph is not left in a * broken state to trigger assertions. Additional tests ensure the correctness of the implementation. * All tests additionally -XX:+AbortVMOnCompilationFailure which would catch bad graphs as a result of missing or wrong Assertion Predicates where we simply bail out of C2 compilation. * @run main/othervm -Xbatch * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure * -XX:CompileCommand=compileonly,compiler.predicates.TestAssertionPredicates::* * -XX:CompileCommand=dontinline,compiler.predicates.TestAssertionPredicates::* * compiler.predicates.TestAssertionPredicates Xbatch */ /* * @test id=NoTieredCompilation * @bug 8288981 8350579 * @run main/othervm -Xbatch -XX:-TieredCompilation * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure * -XX:CompileCommand=compileonly,compiler.predicates.TestAssertionPredicates::* * -XX:CompileCommand=dontinline,compiler.predicates.TestAssertionPredicates::* * compiler.predicates.TestAssertionPredicates NoTieredCompilation */ /* * @test id=Xcomp * @bug 8288981 8350579 * @run main/othervm -Xcomp * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure * -XX:CompileCommand=compileonly,compiler.predicates.TestAssertionPredicates::* * -XX:CompileCommand=dontinline,compiler.predicates.TestAssertionPredicates::* * -XX:CompileCommand=inline,compiler.predicates.TestAssertionPredicates::inline * compiler.predicates.TestAssertionPredicates Xcomp */ /* * @test id=LoopMaxUnroll0 * @bug 8288981 8350579 * @requires vm.compiler2.enabled * @run main/othervm -Xcomp -XX:LoopMaxUnroll=0 * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure * -XX:CompileCommand=compileonly,compiler.predicates.TestAssertionPredicates::* * -XX:CompileCommand=dontinline,compiler.predicates.TestAssertionPredicates::* * compiler.predicates.TestAssertionPredicates LoopMaxUnroll0 */ /* * @test id=StressXcomp * @bug 8288981 8350579 * @requires vm.compiler2.enabled * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN -XX:+StressGCM -XX:+AbortVMOnCompilationFailure * -XX:CompileCommand=compileonly,compiler.predicates.TestAssertionPredicates::* * -XX:CompileCommand=dontinline,compiler.predicates.TestAssertionPredicates::* * compiler.predicates.TestAssertionPredicates Stress */ /* * @test id=StressXbatch * @bug 8288981 8350579 * @requires vm.compiler2.enabled * @run main/othervm -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN -XX:+StressGCM -XX:+AbortVMOnCompilationFailure * -XX:CompileCommand=compileonly,compiler.predicates.TestAssertionPredicates::* * -XX:CompileCommand=dontinline,compiler.predicates.TestAssertionPredicates::* * compiler.predicates.TestAssertionPredicates Stress */ /* * @test id=NoLoopPredication * @bug 8288981 8350579 * @requires vm.compiler2.enabled * @run main/othervm -Xbatch -XX:-UseLoopPredicate * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure * -XX:CompileCommand=compileonly,compiler.predicates.TestAssertionPredicates::* * -XX:CompileCommand=dontinline,compiler.predicates.TestAssertionPredicates::* * compiler.predicates.TestAssertionPredicates NoLoopPredication */ /* * @test id=NoFlags * @bug 8288981 8350579 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure * compiler.predicates.TestAssertionPredicates NoFlags */ package compiler.predicates; public class TestAssertionPredicates { static int[] iArrShort = new int[10]; static int[] iArr = new int[200]; static int[] iArr2 = new int[200]; static int[] iArr3 = new int[300]; static short[] sArr = new short[10]; static boolean flag; static int iFld = 34; static int iFld2, iFld3; static float fFld; static short five = 5; public static void main(String[] args) { switch (args[0]) { case "Xbatch" -> { for (int i = 0; i < 10000; i++) { flag = !flag; testTemplateAssertionPredicateNotRemovedHalt(); testTemplateAssertionPredicateNotRemovedMalformedGraph(); test8305428(); test8305428No2(); } } case "NoTieredCompilation" -> { for (int i = 0; i < 1000; i++) { test8320920(); test8332501(); } } case "LoopMaxUnroll0" -> { testDyingRuntimePredicate(); testDyingNegatedRuntimePredicate(); } case "Xcomp" -> { testDyingInitializedAssertionPredicate(); } case "NoLoopPredication", "NoFlags", "Stress" -> { for (int i = 0; i < 10000; i++) { runAllTests(); } } default -> throw new RuntimeException("invalid arg"); } } static void runAllTests() { testTemplateAssertionPredicateNotRemovedHalt(); testTemplateAssertionPredicateNotRemovedMalformedGraph(); testDyingRuntimePredicate(); testDyingNegatedRuntimePredicate(); testDyingInitializedAssertionPredicate(); test8305428(); test8305428No2(); test8320920(); test8332501(); } // Corresponds to JDK-8305428. // -Xbatch -XX:CompileCommand=compileonly,Test*::* public static void testTemplateAssertionPredicateNotRemovedHalt() { int one = 34; int limit = 2; for (; limit < 4; limit *= 2); for (int i = 2; i < limit; i++) { one = 1; } int i = 80; // 1) Found to be a counted loop with a limit between 1 and 34 (C2 only knows after CCP that 'one' // is the constant 1). // 2b) Two Hoisted Range Check Predicates. // Two Template Assertion Predicates but triggering this bug, we only need to focus on one: // Init Value: OpaqueLoopInit(0) = 5; i--) { fFld += 34; } // Note that this loop does not have any Parse Predicates above its loop entry. // 5) This loop is converted to a counted loop. // 6) We pre-main-post this loop and update the Template Assertion Predicate for the main-loop // The problem now is, that we use the init value of this loop which is completely // unrelated to the init value of the outer loop for which this Tempalte Asseriton Predicate // was originally created! We get the following wrong Template Assertion Predicate for the // init value: // OpaqueLoopInit(79) head not cloned. // As a result, this head has the safepoint as backedge instead of the loop exit test // and we cannot create a counted loop (yet). We first need to partial peel. if (flag) { } // Loop exit test. if (i < 5) { break; } // <-- Partial Peeling CUT --> // Safepoint fFld += 34; // Make sure loop not empty. i--; } // 2a) Loop Predication hoists this check out of the loop with two Hoisted Range Check // Predicates and two Template Assertion Predicates at 2b). iArrShort[j] = 3; } } // Corresponds to JDK-8305428 but with different manifestation (malformed graph). // -Xbatch -XX:CompileCommand=compileonly,Test*::* public static void testTemplateAssertionPredicateNotRemovedMalformedGraph() { int zero = 34; int limit = 2; for (; limit < 4; limit *= 2); for (int i = 2; i < limit; i++) { zero = 1; } int i = 80; for (int j = 0; j < zero; j++) { while (true) { // Found as loop head in ciTypeFlow, but both paths inside loop -> head not cloned. // As a result, this head has the safepoint as backedge instead of the loop exit test // and we cannot create a counted loop (yet). We first need to partial peel. if (flag) { } // Loop exit test. if (i < -5) { break; } // <-- Partial Peeling CUT --> // Safepoint fFld = iArr2[i+5]; i--; } iArr[j] = 3; } } /* * Some tests catching some issues while adding the new implementation. */ // Initialized Assertion Predicate with ConI as bool node is not recognized, and we miss to remove a Template // Assertion Predicate from which we later create a wrong Initialized Assertion Predicate (for wrong loop). static void testDyingInitializedAssertionPredicate() { boolean b = false; int i4, i6, i7 = 14, i8, iArr[][] = new int[10][10]; for (int i = 0; i < iArr.length; i++) { inline(iArr[i]); } for (i4 = 7; i4 < 10; i4++) { iArr2[1] += 5; } for (i6 = 100; i6 > 4; --i6) { i8 = 1; while (++i8 < 6) { sArr[i8] = 3; i7 += i8 + i8; iArr2[i8] -= 34; } } } public static void inline(int[] a) { for (int i = 0; i < a.length; i++) { a[i] = 4; } } static void testDyingRuntimePredicate() { int zero = 34; int[] iArrLoc = new int[100]; int limit = 2; int loopInit = -10; int four = -10; for (; limit < 4; limit *= 2); for (int i = 2; i < limit; i++) { zero = 0; loopInit = 99; four = 4; } // Template + Hoisted Range Check Predicate for iArr[i]. Hoisted Invariant Check Predicate IC for iArr3[index]. // After CCP: ConI for IC and uncommon proj already killed -> IGVN will fold this away. But Predicate logic // need to still recognize this predicate to find the Template above to kill it. If we don't do it, then it // will end up at loop below and peeling will clone the template and create a completely wrong Initialized // Assertion Predicate, killing some parts of the graph and leaving us with a broken graph. for (int i = loopInit; i < 100; i++) { iArr[i] = 34; iArrLoc[four] = 34; } int i = -10; while (true) { // Found as loop head in ciTypeFlow, but both paths inside loop -> head not cloned. // As a result, this head has the safepoint as backedge instead of the loop exit test // and we cannot create a counted loop (yet). We first need to partial peel. if (zero * i == 34) { iFld2 = 23; } else { iFld = 2; } // Loop exit test. if (i >= -2) { break; } // <-- Partial Peeling CUT --> // Safepoint if (zero * i + five == 0) { return; } iFld2 = 34; i++; } } static void testDyingNegatedRuntimePredicate() { int zero = 34; int[] iArrLoc = new int[100]; int limit = 2; int loopInit = -10; int four = -10; for (; limit < 4; limit *= 2); for (int i = 2; i < limit; i++) { zero = 0; loopInit = 99; four = 4; } // Template + Hoisted Range Check Predicate for iArr[i]. Hoisted Invariant Check Predicate IC for iArr3[index]. // After CCP: ConI for IC and uncommon proj already killed -> IGVN will fold this away. But Predicate logic // need to still recognize this predicate to find the Template above to kill it. If we don't do it, then it // will end up at loop below and peeling will clone the template and create a completely wrong Initialized // Assertion Predicate, killing some parts of the graph and leaving us with a broken graph. for (int i = loopInit; i < 100; i++) { iArr[i] = 34; if (-3 > loopInit) { // Negated Hoisted Invariant Check Predicate. iArrLoc[101] = 34; // Always out of bounds and will be a range_check trap in the graph. } } int i = -10; while (true) { // Found as loop head in ciTypeFlow, but both paths inside loop -> head not cloned. // As a result, this head has the safepoint as backedge instead of the loop exit test // and we cannot create a counted loop (yet). We first need to partial peel. if (zero * i == 34) { iFld2 = 23; } else { iFld = 2; } // Loop exit test. if (i >= -2) { break; } // <-- Partial Peeling CUT --> // Safepoint if (zero * i + five == 0) { return; } iFld2 = 34; i++; } } /** * Tests collected in JBS and duplicated issues */ // -Xbatch -XX:CompileCommand=compileonly,Test*::* static void test8305428() { int j = 1; do { for (int k = 270; k > 1; --k) { iFld++; } switch (j) { case 1: switch (92) { case 92: flag = flag; } case 2: iArr[j] = 3; } } while (++j < 100); } // -Xbatch -XX:CompileCommand=compileonly,Test*::* static void test8305428No2() { int i = 1; do { for (int j = 103; j > 1; --j) { iArr[i] = iArr[j / 34]; } for (int j = 103; j > 4; j -= 3) { switch (i % 9) { case 0: case 2: case 3: iArr[i - 1] = 34; case 8: } } } while (++i < 99); } static void test8320920() { int i = 1; do { for (int j = 83; j > i; j--) { iFld = 3; } for (int j = 5; j < 83; j++) { for (int k = i; k < 2; k++) ; } iArr3[i - 1] = 34; } while (++i < 300); } static void test8332501() { int i = 1; do { for (int j = 108; j > 1; j -= 2) { fFld += j; } for (int j = 3; 108 > j; j++) { for (int k = 2; k > i; --k) { } } iArr[i] = 34; } while (++i < 150); } }