/*
 * Copyright (c) 2023, 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
 * @bug 8280126
 * @compile TestDeadIrreducibleLoops.jasm
 * @summary Irreducible loops have many entries, only when the last entry loses
 *          control from the outside does the loop die, and have to disappear.
 * @run main/othervm
 *      -XX:CompileCommand=compileonly,TestDeadIrreducibleLoopsMain::test*
 *      -XX:CompileCommand=compileonly,TestDeadIrreducibleLoops::test*
 *      -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN
 *      TestDeadIrreducibleLoopsMain
 */

/*
 * @test
 * @bug 8280126
 * @compile TestDeadIrreducibleLoops.jasm
 * @summary Irreducible loops have many entries, only when the last entry loses
 *          control from the outside does the loop die, and have to disappear.
 * @run main/othervm
 *      -XX:CompileCommand=compileonly,TestDeadIrreducibleLoopsMain::test*
 *      -XX:CompileCommand=compileonly,TestDeadIrreducibleLoops::test*
 *      -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN
 *      -XX:PerMethodTrapLimit=0
 *      TestDeadIrreducibleLoopsMain
 */

/*
 * @test
 * @bug 8280126
 * @compile TestDeadIrreducibleLoops.jasm
 * @summary Irreducible loops have many entries, only when the last entry loses
 *          control from the outside does the loop die, and have to disappear.
 * @run main/othervm
 *      -XX:CompileCommand=compileonly,TestDeadIrreducibleLoopsMain::test*
 *      -XX:CompileCommand=compileonly,TestDeadIrreducibleLoops::test*
 *      -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN
 *      -Xcomp -XX:-TieredCompilation
 *      TestDeadIrreducibleLoopsMain
 */

/*
 * @test
 * @bug 8280126
 * @compile TestDeadIrreducibleLoops.jasm
 * @summary Irreducible loops have many entries, only when the last entry loses
 *          control from the outside does the loop die, and have to disappear.
 * @run main/othervm
 *      -XX:CompileCommand=compileonly,TestDeadIrreducibleLoopsMain::test*
 *      -XX:CompileCommand=compileonly,TestDeadIrreducibleLoops::test*
 *      -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN
 *      -Xcomp -XX:-TieredCompilation
 *      -XX:PerMethodTrapLimit=0
 *      TestDeadIrreducibleLoopsMain
 */

// Note: if this test fails intermittently, then use -XX:RepeatCompilation=1000
// The tests are run in no particular order. If an earlier test fails, a later one
// may fail too and be easier to debug.

public class TestDeadIrreducibleLoopsMain {
   static public void main(String[] args) {
        TestDeadIrreducibleLoops t = new TestDeadIrreducibleLoops();
        test_000(false, false);
        t.test_001(0, 0, 0, 0);
        t.test_002(-1);
        t.test_003(255);
        t.test_004("I am an object\n");
        t.test_005(0, 0);
        t.test_006(0);
        t.test_007(0, 0, 0);
        t.test_008(0, 0, 0);
        t.test_009(0, 0, 0, 0, 0, 0, 0);
        t.test_010(0, 0, 0, 0, 0);
        t.test_011(0, 0, 0, 0, 0);
        t.test_012a(0, 0, 0, 0, 0, 0, 0, 0);
        t.test_012b(0, 0, 0, 0, 0);
        t.test_013(0, 0, 0, 0, 0, 0, 0, 0, 0);
        t.test_014a(0, 0, 0);
        t.test_014b(0, 0, 0);
        int x = t.test_015a(123);
        int y = t.test_015b(123);
        assert x == y: "pow(3,x)";
        t.test_016(0, 0, 0);
        t.test_017(0, 0, 0);
        t.test_018(0);
        t.test_019(0, 0, 0, 0);
        t.test_020(0, 0, 0, 0, 0);
        t.test_021(0, 0, 0, 0, 0, 0);
        t.test_022a(0, 0, 0, 0);
        t.test_022b(0, 0);
        t.test_023(0);
        t.test_024();
        test_025a(false);
        test_025b(false, false);
    }

    public static float test_000(boolean flag1, boolean flag2) {
        float ret = 1.0f;
        int x = 0;
        LOOP1:
        for (int i = 1; i < 1000000; i *= 2) { // about 20 iterations
            if (i % 5 != 0) { // SKIP1
                LOOP2:
                for (int j = 1; j < 1000000; j *= 2) { // about 20 iterations
                    if (j % 5 != 0) { // SKIP2
                        if (x == 0) { // eventually always false -> continue statements float out of loop
                            ret *= 1.0001;
                            if (j > 100) {
                                LOOP3:
                                for (float m = 1.0f; m < 30000.0f; m *= 1.0001f) {
                                    // OSR starts here - should do more than 100k iterations
                                    ret *= 0.99999f;
                                }
                                x = 1;
                            }
                            int y = 77;
                            for (int e = 0; e < 77; e++) {
                                y -= x; // empty_loop, once we know that x == 1
                            }
                            if (y == 0) {
                                // always true after OSR -> cut off ENTRY1 and ENTRY2
                                return ret;
                            }
                            ret += 0.01;
                            if (ret > 20000) {
                                ret = 7.0f;
                                continue LOOP1; // ENTRY1
                            }
                            // back to LOOP2 -> ENTRY2
                        } // end if (x == 0)
                    } // end SKIP2
                } // end LOOP2
            } // end SKIP1
        } // end LOOP1
        return ret;
    }

    static float test_025a(boolean flag) {
        // Based on test_000, but much simplified.
        // Irreducible loop with OSR. Inlining in irreducible loop.
        float ret = 3.0f;
        LOOP1:
        for (long i = 1; i < 1000_000_000_000L; i *= 2) {
            ret = test_025_inline(ret); // inline region
            LOOP2:
            for (long j = 1; j < 1000_000_000_000L; j *= 2) {
                for (int e = 0; e < 77; e++) {}
                if (flag) {
                    continue LOOP1; // ENTRY1
                }
                // back to LOOP2 -> ENTRY2
            } // end LOOP2
        } // end LOOP1
        return ret;
    }

    static float test_025b(boolean flag1, boolean flag2) {
        // Based on test_000.
        // Irreducible loop with OSR. Inlining in irreducible loop.
        float ret = 1.0f;
        int x = 0;
        LOOP1:
        for (long i = 1; i < 1000_000_000_000L; i *= 2) {
            ret = test_025_inline(ret);
            if (i % 5 != 0) { // SKIP1
                LOOP2:
                for (long j = 1; j < 1000_000_000_000L; j *= 2) {
                    if (j % 5 != 0) { // SKIP2
                        if (x == 0) { // eventually always false -> continue statements float out of loop
                            ret *= 1.0001;
                            if (i > 1000_000_000L) {
                                LOOP3:
                                for (float m = 1.0f; m < 30000.0f; m *= 1.0001f) {
                                    // OSR starts here - should do more than 100k iterations
                                    ret *= 0.99999f;
                                }
                                x = 1;
                            }
                            int y = 77;
                            for (int e = 0; e < 77; e++) {
                                y -= x; // empty_loop, once we know that x == 1
                            }
                            if (y == 0) {
                                // always true after OSR -> cut off ENTRY1 and ENTRY2
                                return ret;
                            }
                            ret += 0.01;
                            if (ret > 20000) {
                                ret = 7.0f;
                                continue LOOP1; // ENTRY1
                            }
                            // back to LOOP2 -> ENTRY2
                        } // end if (x == 0)
                    } // end SKIP2
                } // end LOOP2
            } // end SKIP1
        } // end LOOP1
        return ret;
    }

    static float test_025_inline(float x) {
        if (x >= 1.0f) {
          x *= 0.5f;
        } else {
          x *= 2.0f;
        }
        // Region to merge the if
        return x;
    }
}