/* * Copyright (c) 2024, 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 8332920 * @summary Tests partial peeling at unsigned tests with limit being negative in exit tests "i >u limit". * @run main/othervm -Xbatch -XX:-TieredCompilation * -XX:CompileOnly=*TestPartialPeel*::original*,*TestPartialPeel*::test* * compiler.loopopts.TestPartialPeelAtUnsignedTestsNegativeLimit */ /* * @test id=Xcomp-run-inline * @bug 8332920 * @summary Tests partial peeling at unsigned tests with limit being negative in exit tests "i >u limit". * @run main/othervm -Xcomp -XX:-TieredCompilation * -XX:CompileOnly=*TestPartialPeel*::original*,*TestPartialPeel*::run*,*TestPartialPeel*::test* * -XX:CompileCommand=inline,*TestPartialPeelAtUnsignedTestsNegativeLimit::test* * -XX:CompileCommand=dontinline,*TestPartialPeelAtUnsignedTestsNegativeLimit::check * compiler.loopopts.TestPartialPeelAtUnsignedTestsNegativeLimit */ /* * @test id=Xcomp-compile-test * @bug 8332920 * @summary Tests partial peeling at unsigned tests with limit being negative in exit tests "i >u limit". * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:CompileOnly=*TestPartialPeel*::original*,*TestPartialPeel*::test* * compiler.loopopts.TestPartialPeelAtUnsignedTestsNegativeLimit */ /* * @test id=vanilla * @bug 8332920 * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) * @summary Tests partial peeling at unsigned tests with limit being negative in exit tests "i >u limit". * Only run this test with C2 since it is time-consuming and only tests a C2 issue. * @run main compiler.loopopts.TestPartialPeelAtUnsignedTestsNegativeLimit */ package compiler.loopopts; import java.util.Random; import static java.lang.Integer.*; public class TestPartialPeelAtUnsignedTestsNegativeLimit { static int iFld = 10000; static int iterations = 0; static int iFld2; static boolean flag; final static Random RANDOM = new Random(); public static void main(String[] args) { compareUnsigned(3, 3); // Load Integer class for -Xcomp for (int i = 0; i < 2; i++) { if (!originalTest()) { throw new RuntimeException("originalTest() failed"); } } for (int i = 0; i < 2000; i++) { // For profiling iFld = -1; originalTestVariation1(); // Actual run iFld = MAX_VALUE - 100_000; if (!originalTestVariation1()) { throw new RuntimeException("originalTestVariation1() failed"); } } for (int i = 0; i < 2000; ++i) { // For profiling iFld = MAX_VALUE; originalTestVariation2(); // Actual run iFld = MIN_VALUE + 100000; if (!originalTestVariation2()) { throw new RuntimeException("originalTestVariation2() failed"); } } runWhileLTIncr(); runWhileLTDecr(); } // Originally reported simplified regression test with 2 variations (see below). public static boolean originalTest() { for (int i = MAX_VALUE - 50_000; compareUnsigned(i, -1) < 0; i++) { if (compareUnsigned(MIN_VALUE, i) < 0) { return true; } } return false; } public static boolean originalTestVariation1() { int a = 0; for (int i = iFld; compareUnsigned(i, -1) < 0; ++i) { // i <u -1 if (i >= Integer.MIN_VALUE + 1 && i <= 100) { // Transformed to unsigned test. return true; } a *= 23; } return false; } public static boolean originalTestVariation2() { int a = 0; for (int i = iFld; compareUnsigned(i, -1000) < 0; i--) { // i <u -1 if (compareUnsigned(MAX_VALUE - 20, i) > 0) { return true; } a = i; } System.out.println(a); return false; } public static void testWhileLTIncr(int init, int limit) { int i = init; while (true) { // <Peeled Section> // 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) { } iFld2++; // Loop exit test i >=u limit (i.e. "while (i <u limit)") to partial peel with. // insert_cmpi_loop_exit() changes this exit condition into a signed and an unsigned test: // i >= limit && i >=u limit // where the signed condition can be used as proper loop exit condition for a counted loop // (we cannot use an unsigned counted loop exit condition). // // After Partial Peeling, we have: // if (i >= limit) goto Exit // Loop: // if (i >=u limit) goto Exit // ... // i++; // if (i >= limit) goto Exit // goto Loop // Exit: // ... // // If init = MAX_VALUE and limit = MIN_VALUE: // i >= limit // MAX_VALUE >= MIN_VALUE // which is true where // i >=u limit // MAX_VALUE >=u MIN_VALUE // MAX_VALUE >=u (uint)(MAX_INT + 1) // is false and we wrongly never enter the loop even though we should have. // This results in a wrong execution. if (compareUnsigned(i, limit) >= 0) { return; } // <-- Partial Peeling CUT --> // Safepoint // <Unpeeled Section> iterations++; i++; } } // Same as testWhileLTIncr() but with decrement instead. public static void testWhileLTDecr(int init, int limit) { int i = init; while (true) { if (flag) { } // Loop exit test. if (compareUnsigned(i, limit) >= 0) { // While (i <u limit) return; } iterations++; i--; } } public static void runWhileLTIncr() { // Currently works: testWhileLTIncr(MAX_VALUE, -1); check(MIN_VALUE); // MAX_VALUE + 1 iterations testWhileLTIncr(-1, 1); check(0); testWhileLTIncr(0, 0); check(0); checkIncrWithRandom(0, 0); // Sanity check this method. flag = !flag; // Change profiling testWhileLTIncr(MAX_VALUE - 2000, MAX_VALUE); check(2000); testWhileLTIncr(MAX_VALUE - 1990, MAX_VALUE); check(1990); testWhileLTIncr(MAX_VALUE - 1, MAX_VALUE); check(1); testWhileLTIncr(MIN_VALUE, MIN_VALUE + 2000); check(2000); testWhileLTIncr(MIN_VALUE, MIN_VALUE + 1990); check(1990); testWhileLTIncr(MIN_VALUE, MIN_VALUE + 1); check(1); flag = !flag; // Overflow currently does not work with negative limit and is fixed with patch: testWhileLTIncr(MAX_VALUE, MIN_VALUE); check(1); testWhileLTIncr(MAX_VALUE - 2000, MIN_VALUE); check(2001); testWhileLTIncr(MAX_VALUE, MIN_VALUE + 2000); check(2001); testWhileLTIncr(MAX_VALUE - 2000, MIN_VALUE + 2000); check(4001); // Random values int init = RANDOM.nextInt(0, MAX_VALUE); int limit = RANDOM.nextInt(MIN_VALUE, 0); testWhileLTIncr(init, limit); checkIncrWithRandom(init, limit); } public static void runWhileLTDecr() { // Currently works: testWhileLTDecr(1, -1); check(2); testWhileLTDecr(-1, 1); check(0); testWhileLTDecr(0, 0); check(0); checkDecrWithRandom(0, 0); // Sanity check this method. flag = !flag; testWhileLTDecr(MAX_VALUE, MIN_VALUE); check(MIN_VALUE); // MAX_VALUE + 1 iterations testWhileLTDecr(MAX_VALUE, -1); check(MIN_VALUE); // MAX_VALUE + 1 iterations testWhileLTDecr(MAX_VALUE, MIN_VALUE); check(MIN_VALUE); // MAX_VALUE + 1 iterations testWhileLTDecr(MIN_VALUE, 0); check(0); testWhileLTDecr(MIN_VALUE, 1); check(0); flag = !flag; // Underflow currently does not work with negative limit and is fixed with patch: testWhileLTDecr(MIN_VALUE, -1); check(MIN_VALUE + 1); // MAX_VALUE + 2 iterations testWhileLTDecr(MIN_VALUE, -2000); check(MIN_VALUE + 1); // MAX_VALUE + 2 iterations testWhileLTDecr(MIN_VALUE, MIN_VALUE + 1); check(MIN_VALUE + 1); // MAX_VALUE + 2 iterations testWhileLTDecr(MIN_VALUE + 2000, -1); check(MIN_VALUE + 2001); // MAX_VALUE + 2002 iterations testWhileLTDecr(MIN_VALUE + 2000, -2000); check(MIN_VALUE + 2001); // MAX_VALUE + 2002 iterations testWhileLTDecr(MIN_VALUE + 2000, MIN_VALUE + 2001); check(MIN_VALUE + 2001); // MAX_VALUE + 2002 iterations // Random values int r1 = RANDOM.nextInt(MIN_VALUE, 0); int r2 = RANDOM.nextInt(MIN_VALUE, 0); int init = Math.min(r1, r2); int limit = Math.max(r1, r2); testWhileLTDecr(init, limit); checkDecrWithRandom(init, limit); } static void check(int expectedIterations) { if (expectedIterations != iterations) { throw new RuntimeException("Expected " + expectedIterations + " iterations but only got " + iterations); } iterations = 0; // Reset } static void checkIncrWithRandom(long init, long limit) { long expectedIterations = ((long)(MAX_VALUE) - init) + (limit - (long)MIN_VALUE) + 1; if ((int)expectedIterations != iterations) { String error = "Expected %d iterations but only got %d, init: %d, limit: %d" .formatted(expectedIterations, iterations, init, limit); throw new RuntimeException(error); } iterations = 0; // Reset } static void checkDecrWithRandom(long init, long limit) { long expectedIterations = init + MIN_VALUE + MAX_VALUE + 2; if (init == limit) { expectedIterations = 0; } if ((int)expectedIterations != iterations) { String error = "Expected %d iterations but only got %d, init: %d, limit: %d" .formatted(expectedIterations, iterations, init, limit); throw new RuntimeException(error); } iterations = 0; // Reset } }