/*
 * Copyright (c) 2014, 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 org.openjdk.bench.vm.compiler;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;

import java.util.concurrent.TimeUnit;

/**
 * Test spill code generation.
 */
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Warmup(iterations = 4, time = 2, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 4, time = 2, timeUnit = TimeUnit.SECONDS)
@Fork(value = 3)
public class SpillCode {

    @Param("10")
    private int iterations;

    private int dummy;

    private int doSomeCalcsInLargeBlockWithInts(int start, int iter) {

        int a1, a2, a3, a4;
        int b1, b2, b3, b4;
        int c1, c2, c3, c4;
        int d1, d2, d3, d4;
        int e1, e2, e3, e4;
        int f1, f2, f3, f4;
        int g1, g2, g3, g4;
        int h1, h2, h3, h4;

        // a1 = b1 = c1 = d1 = e1 = f1 = g1 = h1 = (start % 2);
        a2 = b2 = c2 = d2 = e2 = f2 = g2 = h2 = (start % 2);
        a3 = b3 = c3 = d3 = e3 = f3 = g3 = h3 = (start % 4);
        a4 = b4 = c4 = d4 = e4 = f4 = g4 = h4 = (start % 8);

        for (int i = 0; i < iter; i++) {

            // for each section, only x1 needs to survive.

            // a1 = a2 = a3 = a4 = (start + a1) % 2;
            a1 = start;
            a1 = a1 + a2 + a3 + a4;
            a2 = a1 + a2 + a3 + a4;
            a3 = a1 + a2 + a3 + a4;
            a4 = a1 + a2 + a3 + a4;
            a1 = a1 + a2 + a3 + a4;
            a2 = a1 + a2 + a3 + a4;
            a3 = a1 + a2 + a3 + a4;
            a4 = a1 + a2 + a3 + a4;
            a1 = a1 + a2 + a3 + a4;
            a2 = a1 + a2 + a3 + a4;
            a3 = a1 + a2 + a3 + a4;
            a4 = a1 + a2 + a3 + a4;

            // b1 = b2 = b3 = b4 = (a4 + b1) % 2;
            b1 = a4;
            b1 = b1 + b2 + b3 + b4;
            b2 = b1 + b2 + b3 + b4;
            b3 = b1 + b2 + b3 + b4;
            b4 = b1 + b2 + b3 + b4;
            b1 = b1 + b2 + b3 + b4;
            b2 = b1 + b2 + b3 + b4;
            b3 = b1 + b2 + b3 + b4;
            b4 = b1 + b2 + b3 + b4;
            b1 = b1 + b2 + b3 + b4;
            b2 = b1 + b2 + b3 + b4;
            b3 = b1 + b2 + b3 + b4;
            b4 = b1 + b2 + b3 + b4;

            // c1 = c2 = c3 = c4 = (b4 + c1) % 2;
            c1 = b4;
            c1 = c1 + c2 + c3 + c4;
            c2 = c1 + c2 + c3 + c4;
            c3 = c1 + c2 + c3 + c4;
            c4 = c1 + c2 + c3 + c4;
            c1 = c1 + c2 + c3 + c4;
            c2 = c1 + c2 + c3 + c4;
            c3 = c1 + c2 + c3 + c4;
            c4 = c1 + c2 + c3 + c4;
            c1 = c1 + c2 + c3 + c4;
            c2 = c1 + c2 + c3 + c4;
            c3 = c1 + c2 + c3 + c4;
            c4 = c1 + c2 + c3 + c4;

            // d1 = d2 = d3 = d4 = (c4 + d1) % 2;
            d1 = c4;
            d1 = d1 + d2 + d3 + d4;
            d2 = d1 + d2 + d3 + d4;
            d3 = d1 + d2 + d3 + d4;
            d4 = d1 + d2 + d3 + d4;
            d1 = d1 + d2 + d3 + d4;
            d2 = d1 + d2 + d3 + d4;
            d3 = d1 + d2 + d3 + d4;
            d4 = d1 + d2 + d3 + d4;
            d1 = d1 + d2 + d3 + d4;
            d2 = d1 + d2 + d3 + d4;
            d3 = d1 + d2 + d3 + d4;
            d4 = d1 + d2 + d3 + d4;

            // e1 = e2 = e3 = e4 = (d4 + e1) % 2;
            e1 = d4;
            e1 = e1 + e2 + e3 + e4;
            e2 = e1 + e2 + e3 + e4;
            e3 = e1 + e2 + e3 + e4;
            e4 = e1 + e2 + e3 + e4;
            e1 = e1 + e2 + e3 + e4;
            e2 = e1 + e2 + e3 + e4;
            e3 = e1 + e2 + e3 + e4;
            e4 = e1 + e2 + e3 + e4;
            e1 = e1 + e2 + e3 + e4;
            e2 = e1 + e2 + e3 + e4;
            e3 = e1 + e2 + e3 + e4;
            e4 = e1 + e2 + e3 + e4;

            // f1 = f2 = f3 = f4 = (e4 + f1) % 2;
            f1 = e4;
            f1 = f1 + f2 + f3 + f4;
            f2 = f1 + f2 + f3 + f4;
            f3 = f1 + f2 + f3 + f4;
            f4 = f1 + f2 + f3 + f4;
            f1 = f1 + f2 + f3 + f4;
            f2 = f1 + f2 + f3 + f4;
            f3 = f1 + f2 + f3 + f4;
            f4 = f1 + f2 + f3 + f4;
            f1 = f1 + f2 + f3 + f4;
            f2 = f1 + f2 + f3 + f4;
            f3 = f1 + f2 + f3 + f4;
            f4 = f1 + f2 + f3 + f4;

            // g1 = g2 = g3 = g4 = (f4 + g1) % 2;
            g1 = f4;
            g1 = g1 + g2 + g3 + g4;
            g2 = g1 + g2 + g3 + g4;
            g3 = g1 + g2 + g3 + g4;
            g4 = g1 + g2 + g3 + g4;
            g1 = g1 + g2 + g3 + g4;
            g2 = g1 + g2 + g3 + g4;
            g3 = g1 + g2 + g3 + g4;
            g4 = g1 + g2 + g3 + g4;
            g1 = g1 + g2 + g3 + g4;
            g2 = g1 + g2 + g3 + g4;
            g3 = g1 + g2 + g3 + g4;
            g4 = g1 + g2 + g3 + g4;

            // h1 = h2 = h3 = h4 = (g4 + h1) % 2;
            h1 = g4;
            h1 = h1 + h2 + h3 + h4;
            h2 = h1 + h2 + h3 + h4;
            h3 = h1 + h2 + h3 + h4;
            h4 = h1 + h2 + h3 + h4;
            h1 = h1 + h2 + h3 + h4;
            h2 = h1 + h2 + h3 + h4;
            h3 = h1 + h2 + h3 + h4;
            h4 = h1 + h2 + h3 + h4;
            h1 = h1 + h2 + h3 + h4;
            h2 = h1 + h2 + h3 + h4;
            h3 = h1 + h2 + h3 + h4;
            h4 = h1 + h2 + h3 + h4;

            start = h4;
        }

        return start;
    }

    /**
     * The test runs a loop with many local variables. The issue it reproduces is that if handled wrong, too many variables
     * are put on and referenced on stack. The number of iterations is taken from global variable, to prevent static loop
     * unrolling. Many of the variables used in the larger loop are local inside the block and do dnot need to survive from
     * one iteration to the next.
     */
    @Benchmark
    public int testSpillForManyInts() throws Exception {
        return doSomeCalcsInLargeBlockWithInts(dummy, iterations);
    }
}
