/*
 * Copyright (c) 2020, 2021, 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
 * @key stress randomness
 * @bug 8229855 8238812
 * @summary Test jump table with key value that gets out of bounds after loop unrolling.
 * @requires vm.compiler2.enabled
 *
 * @run main/othervm -XX:CompileCommand=dontinline,compiler.c2.TestJumpTable::test*
 *                   -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation -XX:-UseSwitchProfiling
 *                   compiler.c2.TestJumpTable
 * @run main/othervm -XX:CompileCommand=dontinline,compiler.c2.TestJumpTable::test*
 *                   -Xbatch -XX:-TieredCompilation -XX:-UseOnStackReplacement
 *                   compiler.c2.TestJumpTable
 * @run main/othervm -XX:CompileCommand=dontinline,compiler.c2.TestJumpTable::test*
 *                   -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation -XX:+StressIGVN
 *                   compiler.c2.TestJumpTable
 */

package compiler.c2;

public class TestJumpTable {

    public static int test0() {
        int res = 0;
        for (int i = 10; i < 50; ++i) {
            switch (i * 5) {
                case 15:
                case 25:
                case 40:
                case 101:
                    return 42;
                case 45:
                case 51:
                case 60:
                    res++;
                    break;
            }
        }
        return res;
    }

    static int field;

    // Original (slightly simplified) fuzzer generated test
    public static void test1() {
        int i4, i5 = 99, i6, i9 = 89;
        for (i4 = 12; i4 < 365; i4++) {
            for (i6 = 5; i6 > 1; i6--) {
                switch ((i6 * 5) + 11) {
                case 13:
                case 19:
                case 26:
                case 31:
                case 35:
                case 41:
                case 43:
                case 61:
                case 71:
                case 83:
                case 314:
                    i9 = i5;
                    break;
                }
            }
        }
    }

    // This generates the following subgraph:
    /*
        // i: -10..4
        if ((i+min_jint) u<= max_jint) {    <- This is always true but not folded by C2
            ...
        } else {
            ...
            CastII(i-5, 0..45)              <- Replaced by TOP because i-5 range is -15..-1 but still considered reachable by C2 although it is dead code
            ...
        }
    */
    public static void test2() {
        for (int i = 5; i > -10; i--) {
            switch (i) {
            case 0:
            case 4:
            case 10:
            case 20:
            case 30:
            case 40:
            case 50:
            case 100:
                field = 42;
                break;
            }
        }
    }

    // This generates the following subgraph:
    /*
        // i: -20..0
        if (i != 0) {
            // i: -20..-1
            if (i < 0) {                    <- This is always true but not folded by C2
                // Fall through
            } else {
                ...
                CastII(i-1, 0..4)           <- Replaced by TOP because i-1 range is -21..-1 but still considered reachable by C2 although it is dead code
                ...
            }
        } else {
            StoreI                          <- Due to this additional store on, IfNode::has_shared_region returns false and the fold compares optimization does not kick in
        }
    */
    public static void test3() {
        for (int i = 5; i > -20; i -= 5) {
            switch (i) {
            case 0:
            case 10:
            case 20:
            case 30:
            case 40:
            case 50:
            case 60:
            case 100:
                field = 42;
                break;
            }
        }
    }

    // This generates the following subgraph:
    /*
        // i: -20..0
        if (i != 0) {
            // i: -20..-1
            if (i u< 101) {                 <- This is always false but not folded by C2 because CmpU is not handled
                CastII(i-1, 0..49)          <- Replaced by TOP because i-1 range is -21..-1 but still considered reachable by C2 although it is dead code
            } else {
                ...
            }
        } else {
            ...
        }
    */
    public static void test4() {
        int local = 0;
        for (int i = 5; i > -20; i -= 5) {
            switch (i) {
            case 0:
            case 10:
            case 20:
            case 30:
            case 40:
            case 50:
            case 100:
                local = 42;
                break;
            }
        }
    }

    // This generates the following subgraph:
    /*
        // i: 0..20
        if (i != 20) {
            // i: 0..19
            if ((i-20) u< 281) {            <- This is always false but not folded by C2 because the two ifs compare different values
                CastII(i-21, 0..49)         <- Replaced by TOP because i-21 range is -21..-1 but still considered reachable by C2 although it is dead code
            } else {
                ...
            }
        } else {
            ...
        }
    */
    public static void test5() {
        int local;
        for (int i = 25; i > 0; i -= 5) {
            switch (i) {
            case 20:
            case 30:
            case 40:
            case 50:
            case 60:
            case 70:
            case 300:
                local = 42;
                break;
            }
        }
    }

    // This generates the following subgraph:
    /*
        // i: 0..20
        if ((i+10) != 30) {
            // i: 0..19
            if ((i-20) u< 271) {            <- This is always false but not folded by C2 because the two ifs compare different values
                CastII(i-21, 0..4)          <- Replaced by TOP because i-21 range is -21..-1 but still considered reachable by C2 although it is dead code
            } else {
                ...
            }
        } else {
            ...
        }
    */
    public static void test6() {
        int local;
        for (int i = 25; i > 0; i -= 5) {
            switch (i + 10) {
            case 30:
            case 40:
            case 50:
            case 60:
            case 70:
            case 80:
            case 300:
                local = 42;
                break;
            }
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 50_000; ++i) {
            test0();
            test1();
            test2();
            test3();
            test4();
            test5();
            test6();
        }
    }
}