/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Tests for optimizations of break-loops, i.e. loops that break
 * out of a while-true loop when the end condition is satisfied.
 * In particular, the tests focus on break-loops that can be
 * rewritten into regular countable loops (this may improve certain
 * loops generated by the Kotlin compiler for inclusive ranges).
 */
public class Main {

  /// CHECK-START: int Main.breakLoop(int[]) induction_var_analysis (before)
  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                     loop:none
  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                      loop:none
  /// CHECK-DAG: <<One:i\d+>>  IntConstant 1                      loop:none
  /// CHECK-DAG: <<Nil:l\d+>>  NullCheck [<<Par>>]                loop:none
  /// CHECK-DAG: <<Phi:i\d+>>  Phi [<<Zero>>,<<AddI:i\d+>>]       loop:<<Loop:B\d+>> outer_loop:none
  /// CHECK-DAG: <<Bnd:i\d+>>  BoundsCheck [<<Phi>>,{{i\d+}}]     loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:               ArraySet [<<Nil>>,<<Bnd>>,<<One>>] loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<NE:z\d+>>   NotEqual [{{i\d+}},<<Phi>>]        loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:               If [<<NE>>]                        loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<AddI>>      Add [<<Phi>>,<<One>>]              loop:<<Loop>>      outer_loop:none
  //
  /// CHECK-START: int Main.breakLoop(int[]) induction_var_analysis (after)
  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                     loop:none
  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                      loop:none
  /// CHECK-DAG: <<One:i\d+>>  IntConstant 1                      loop:none
  /// CHECK-DAG: <<Nil:l\d+>>  NullCheck [<<Par>>]                loop:none
  /// CHECK-DAG: <<Phi:i\d+>>  Phi [<<Zero>>,<<AddI:i\d+>>]       loop:<<Loop:B\d+>> outer_loop:none
  /// CHECK-DAG: <<LE:z\d+>>   LessThanOrEqual [<<Phi>>,{{i\d+}}] loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:               If [<<LE>>]                        loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Bnd:i\d+>>  BoundsCheck [<<Phi>>,{{i\d+}}]     loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:               ArraySet [<<Nil>>,<<Bnd>>,<<One>>] loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<AddI>>      Add [<<Phi>>,<<One>>]              loop:<<Loop>>      outer_loop:none
  //
  /// CHECK-START-ARM64: int Main.breakLoop(int[]) loop_optimization (after)
  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                          loop:none
  /// CHECK-DAG: <<One:i\d+>>  IntConstant 1                           loop:none
  /// CHECK-DAG: <<Four:i\d+>> IntConstant 4                           loop:none
  /// CHECK-DAG: <<Nil:l\d+>>  NullCheck [<<Par>>]                     loop:none
  /// CHECK-DAG: <<Rep:d\d+>>  VecReplicateScalar [<<One>>]            loop:none
  /// CHECK-DAG:               VecStore [<<Nil>>,<<Phi:i\d+>>,<<Rep>>] loop:<<Loop:B\d+>> outer_loop:none
  /// CHECK-DAG:               Add [<<Phi>>,<<Four>>]                  loop:<<Loop>>      outer_loop:none
  static int breakLoop(int[] a) {
    int l = 0;
    int u = a.length - 1;
    int i = l;
    if (l <= u) {
      while (true) {
        a[i] = 1;
        if (i == u) break;
        i++;
      }
    }
    return i;
  }

  /// CHECK-START: int Main.breakLoopDown(int[]) induction_var_analysis (before)
  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                     loop:none
  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                      loop:none
  /// CHECK-DAG: <<MOne:i\d+>> IntConstant -1                     loop:none
  /// CHECK-DAG: <<Two:i\d+>>  IntConstant 2                      loop:none
  /// CHECK-DAG: <<Nil:l\d+>>  NullCheck [<<Par>>]                loop:none
  /// CHECK-DAG: <<Phi:i\d+>>  Phi [{{i\d+}},<<AddI:i\d+>>]       loop:<<Loop:B\d+>> outer_loop:none
  /// CHECK-DAG: <<Bnd:i\d+>>  BoundsCheck [<<Phi>>,{{i\d+}}]     loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:               ArraySet [<<Nil>>,<<Bnd>>,<<Two>>] loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<NE:z\d+>>   NotEqual [<<Phi>>,<<Zero>>]        loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:               If [<<NE>>]                        loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<AddI>>      Add [<<Phi>>,<<MOne>>]             loop:<<Loop>>      outer_loop:none
  //
  /// CHECK-START: int Main.breakLoopDown(int[]) induction_var_analysis (after)
  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                        loop:none
  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                         loop:none
  /// CHECK-DAG: <<MOne:i\d+>> IntConstant -1                        loop:none
  /// CHECK-DAG: <<Two:i\d+>>  IntConstant 2                         loop:none
  /// CHECK-DAG: <<Nil:l\d+>>  NullCheck [<<Par>>]                   loop:none
  /// CHECK-DAG: <<Phi:i\d+>>  Phi [{{i\d+}},<<AddI:i\d+>>]          loop:<<Loop:B\d+>> outer_loop:none
  /// CHECK-DAG: <<GE:z\d+>>   GreaterThanOrEqual [<<Phi>>,<<Zero>>] loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:               If [<<GE>>]                           loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Bnd:i\d+>>  BoundsCheck [<<Phi>>,{{i\d+}}]        loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:               ArraySet [<<Nil>>,<<Bnd>>,<<Two>>]    loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<AddI>>      Add [<<Phi>>,<<MOne>>]                loop:<<Loop>>      outer_loop:none
  static int breakLoopDown(int[] a) {
    int l = 0;
    int u = a.length - 1;
    int i = u;
    if (u >= l) {
      while (true) {
        a[i] = 2;
        if (i == l) break;
        i--;
      }
    }
    return i;
  }

  /// CHECK-START: int Main.breakLoopSafeConst(int[]) induction_var_analysis (before)
  /// CHECK-DAG: <<Par:l\d+>>   ParameterValue                       loop:none
  /// CHECK-DAG: <<One:i\d+>>   IntConstant 1                        loop:none
  /// CHECK-DAG: <<Three:i\d+>> IntConstant 3                        loop:none
  /// CHECK-DAG: <<L1:i\d+>>    IntConstant 2147483631               loop:none
  /// CHECK-DAG: <<L2:i\d+>>    IntConstant 2147483646               loop:none
  /// CHECK-DAG: <<Phi:i\d+>>   Phi [<<L1>>,<<AddI:i\d+>>]           loop:<<Loop:B\d+>> outer_loop:none
  /// CHECK-DAG: <<Sub:i\d+>>   Sub [<<Phi>>,<<L1>>]                 loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Nil:l\d+>>   NullCheck [<<Par>>]                  loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Bnd:i\d+>>   BoundsCheck [<<Sub>>,{{i\d+}}]       loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:                ArraySet [<<Nil>>,<<Bnd>>,<<Three>>] loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<NE:z\d+>>    NotEqual [<<Phi>>,<<L2>>]            loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:                If [<<NE>>]                          loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<AddI>>       Add [<<Phi>>,<<One>>]                loop:<<Loop>>      outer_loop:none
  //
  /// CHECK-START: int Main.breakLoopSafeConst(int[]) induction_var_analysis (after)
  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                        loop:none
  /// CHECK-DAG: <<One:i\d+>>   IntConstant 1                        loop:none
  /// CHECK-DAG: <<Three:i\d+>> IntConstant 3                        loop:none
  /// CHECK-DAG: <<L1:i\d+>>    IntConstant 2147483631               loop:none
  /// CHECK-DAG: <<L2:i\d+>>    IntConstant 2147483646               loop:none
  /// CHECK-DAG: <<Phi:i\d+>>   Phi [<<L1>>,<<AddI:i\d+>>]           loop:<<Loop:B\d+>> outer_loop:none
  /// CHECK-DAG: <<LE:z\d+>>    LessThanOrEqual [<<Phi>>,<<L2>>]     loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Sub:i\d+>>   Sub [<<Phi>>,<<L1>>]                 loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Nil:l\d+>>   NullCheck [<<Par>>]                  loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Bnd:i\d+>>   BoundsCheck [<<Sub>>,{{i\d+}}]       loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:                ArraySet [<<Nil>>,<<Bnd>>,<<Three>>] loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<AddI>>       Add [<<Phi>>,<<One>>]                loop:<<Loop>>      outer_loop:none
  //
  /// CHECK-START-ARM64: int Main.breakLoopSafeConst(int[]) loop_optimization (after)
  /// CHECK-DAG: <<Par:l\d+>>   ParameterValue                          loop:none
  /// CHECK-DAG: <<One:i\d+>>   IntConstant 1                           loop:none
  /// CHECK-DAG: <<Three:i\d+>> IntConstant 3                           loop:none
  /// CHECK-DAG: <<Four:i\d+>>  IntConstant 4                           loop:none
  /// CHECK-DAG: <<Rep:d\d+>>   VecReplicateScalar [<<Three>>]          loop:none
  /// CHECK-DAG:                VecStore [<<Par>>,<<Phi:i\d+>>,<<Rep>>] loop:<<Loop:B\d+>> outer_loop:none
  /// CHECK-DAG:                Add [<<Phi>>,<<Four>>]                  loop:<<Loop>>      outer_loop:none
  static int breakLoopSafeConst(int[] a) {
    int l = Integer.MAX_VALUE - 16;
    int u = Integer.MAX_VALUE - 1;
    int i = l;
    if (l <= u) {  // will be removed by simplifier
      while (true) {
        a[i - l] = 3;
        if (i == u) break;
        i++;
      }
    }
    return i;
  }

  /// CHECK-START: int Main.breakLoopUnsafeConst(int[]) induction_var_analysis (before)
  /// CHECK-DAG: <<Par:l\d+>>   ParameterValue                       loop:none
  /// CHECK-DAG: <<One:i\d+>>   IntConstant 1                        loop:none
  /// CHECK-DAG: <<Four:i\d+>>  IntConstant 4                        loop:none
  /// CHECK-DAG: <<L1:i\d+>>    IntConstant 2147483632               loop:none
  /// CHECK-DAG: <<L2:i\d+>>    IntConstant 2147483647               loop:none
  /// CHECK-DAG: <<Phi:i\d+>>   Phi [<<L1>>,<<AddI:i\d+>>]           loop:<<Loop:B\d+>> outer_loop:none
  /// CHECK-DAG: <<Sub:i\d+>>   Sub [<<Phi>>,<<L1>>]                 loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Nil:l\d+>>   NullCheck [<<Par>>]                  loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Bnd:i\d+>>   BoundsCheck [<<Sub>>,{{i\d+}}]       loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:                ArraySet [<<Nil>>,<<Bnd>>,<<Four>>]  loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<NE:z\d+>>    NotEqual [<<Phi>>,<<L2>>]            loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:                If [<<NE>>]                          loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<AddI>>       Add [<<Phi>>,<<One>>]                loop:<<Loop>>      outer_loop:none
  //
  /// CHECK-START: int Main.breakLoopUnsafeConst(int[]) induction_var_analysis (after)
  /// CHECK-DAG:                NotEqual
  /// CHECK-NOT:                LessThanOrEqual
  static int breakLoopUnsafeConst(int[] a) {
    int l = Integer.MAX_VALUE - 15;
    int u = Integer.MAX_VALUE;
    int i = l;
    if (l <= u) {  // will be removed by simplifier
      while (true) {
        a[i - l] = 4;
        if (i == u) break;  // rewriting exit not safe!
        i++;
      }
    }
    return i;
  }

  /// CHECK-START: int Main.breakLoopNastyPhi(int[]) induction_var_analysis (before)
  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                      loop:none
  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                       loop:none
  /// CHECK-DAG: <<One:i\d+>>  IntConstant 1                       loop:none
  /// CHECK-DAG: <<Five:i\d+>> IntConstant 5                       loop:none
  /// CHECK-DAG: <<M123:i\d+>> IntConstant -123                    loop:none
  /// CHECK-DAG: <<Nil:l\d+>>  NullCheck [<<Par>>]                 loop:none
  /// CHECK-DAG: <<Phi:i\d+>>  Phi [<<Zero>>,<<AddI:i\d+>>]        loop:<<Loop:B\d+>> outer_loop:none
  /// CHECK-DAG: <<Wrap:i\d+>> Phi [<<M123>>,<<Phi>>]              loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Bnd:i\d+>>  BoundsCheck [<<Phi>>,{{i\d+}}]      loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:               ArraySet [<<Nil>>,<<Bnd>>,<<Five>>] loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<NE:z\d+>>   NotEqual [{{i\d+}},<<Phi>>]         loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:               If [<<NE>>]                         loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<AddI>>      Add [<<Phi>>,<<One>>]               loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Comb:i\d+>> Phi [<<M123>>,<<Wrap>>]             loop:none
  /// CHECK-DAG:               Return [<<Comb>>]                   loop:none
  //
  /// CHECK-START: int Main.breakLoopNastyPhi(int[]) induction_var_analysis (after)
  /// CHECK-DAG:               NotEqual
  /// CHECK-NOT:               LessThanOrEqual
  static int breakLoopNastyPhi(int[] a) {
    int l = 0;
    int u = a.length - 1;
    int x = -123;
    if (l <= u) {
      int i = l;
      while (true) {
        a[i] = 5;
        if (i == u) break;
        x = i;
        i++;
      }
    }
    return x;  // keep another phi live
  }

  /// CHECK-START: int Main.breakLoopReduction(int[]) induction_var_analysis (before)
  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                 loop:none
  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                  loop:none
  /// CHECK-DAG: <<One:i\d+>>  IntConstant 1                  loop:none
  /// CHECK-DAG: <<Nil:l\d+>>  NullCheck [<<Par>>]                 loop:none
  /// CHECK-DAG: <<Red:i\d+>>  Phi [<<Zero>>,<<RedI:i\d+>>]   loop:<<Loop:B\d+>> outer_loop:none
  /// CHECK-DAG: <<Phi:i\d+>>  Phi [<<Zero>>,<<AddI:i\d+>>]   loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Bnd:i\d+>>  BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Get:i\d+>>  ArrayGet [<<Nil>>,<<Bnd>>]     loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<RedI>>      Add [<<Red>>,<<Get>>]          loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<NE:z\d+>>   NotEqual [{{i\d+}},<<Phi>>]    loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:               If [<<NE>>]                    loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<AddI>>      Add [<<Phi>>,<<One>>]          loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Comb:i\d+>> Phi [<<Zero>>,<<RedI>>]        loop:none
  /// CHECK-DAG:               Return [<<Comb>>]              loop:none
  //
  /// CHECK-START: int Main.breakLoopReduction(int[]) induction_var_analysis (after)
  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                     loop:none
  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                      loop:none
  /// CHECK-DAG: <<One:i\d+>>  IntConstant 1                      loop:none
  /// CHECK-DAG: <<Nil:l\d+>>  NullCheck [<<Par>>]                loop:none
  /// CHECK-DAG: <<Red:i\d+>>  Phi [<<Zero>>,<<RedI:i\d+>>]       loop:<<Loop:B\d+>> outer_loop:none
  /// CHECK-DAG: <<Phi:i\d+>>  Phi [<<Zero>>,<<AddI:i\d+>>]       loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<LE:z\d+>>   LessThanOrEqual [<<Phi>>,{{i\d+}}] loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG:               If [<<LE>>]                        loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Bnd:i\d+>>  BoundsCheck [<<Phi>>,{{i\d+}}]     loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Get:i\d+>>  ArrayGet [<<Nil>>,<<Bnd>>]         loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<RedI>>      Add [<<Red>>,<<Get>>]              loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<AddI>>      Add [<<Phi>>,<<One>>]              loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<Comb:i\d+>> Phi [<<Zero>>,<<Red>>]             loop:none
  /// CHECK-DAG:               Return [<<Comb>>]                  loop:none
  //
  /// CHECK-START-ARM64: int Main.breakLoopReduction(int[]) loop_optimization (after)
  /// CHECK-DAG: <<Par:l\d+>>   ParameterValue              loop:none
  /// CHECK-DAG: <<Zero:i\d+>>  IntConstant 0               loop:none
  /// CHECK-DAG: <<Exp:d\d+>>   VecSetScalars [<<Zero>>]    loop:none
  /// CHECK-DAG: <<VPhi:d\d+>>  Phi [<<Exp>>,<<VAdd:d\d+>>] loop:<<Loop:B\d+>> outer_loop:none
  /// CHECK-DAG: <<VLoad:d\d+>> VecLoad                     loop:<<Loop>>      outer_loop:none
  /// CHECK-DAG: <<VAdd>>       VecAdd [<<VPhi>>,<<VLoad>>] loop:<<Loop>>      outer_loop:none
  static int breakLoopReduction(int[] a) {
    int l = 0;
    int u = a.length - 1;
    int x = 0;
    if (l <= u) {
      int i = l;
      while (true) {
        x += a[i];
        if (i == u) break;
        i++;
      }
    }
    return x;
  }

  //
  // Test driver.
  //

  public static void main(String[] args) {
    int[] a = new int[100];

    expectEquals(99, breakLoop(a));
    for (int i = 0; i < a.length; i++) {
      expectEquals(1, a[i]);
    }

    expectEquals(0, breakLoopDown(a));
    for (int i = 0; i < a.length; i++) {
      expectEquals(2, a[i]);
    }

    expectEquals(Integer.MAX_VALUE - 1, breakLoopSafeConst(a));
    for (int i = 0; i < a.length; i++) {
      int e = i < 16 ? 3 : 2;
      expectEquals(e, a[i]);
    }

    expectEquals(Integer.MAX_VALUE, breakLoopUnsafeConst(a));
    for (int i = 0; i < a.length; i++) {
      int e = i < 16 ? 4 : 2;
      expectEquals(e, a[i]);
    }

    expectEquals(98, breakLoopNastyPhi(a));
    for (int i = 0; i < a.length; i++) {
      expectEquals(5, a[i]);
    }

    expectEquals(500, breakLoopReduction(a));

    System.out.println("passed");
  }

  private static void expectEquals(int expected, int result) {
    if (expected != result) {
      throw new Error("Expected: " + expected + ", found: " + result);
    }
  }
}
