/*
 * Copyright (C) 2015 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.
 */

//
// Test on loop optimizations.
//
public class Main {

  static int sResult;

  //
  // Various sequence variables used in bound checks.
  //

  /// CHECK-START: void Main.bubble(int[]) BCE (before)
  /// CHECK-DAG: BoundsCheck
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.bubble(int[]) BCE (after)
  /// CHECK-NOT: BoundsCheck
  /// CHECK-NOT: Deoptimize
  private static void bubble(int[] a) {
    for (int i = a.length; --i >= 0;) {
      for (int j = 0; j < i; j++) {
        if (a[j] > a[j+1]) {
          int tmp = a[j];
          a[j]  = a[j+1];
          a[j+1] = tmp;
        }
      }
    }
  }

  /// CHECK-START: int Main.periodicIdiom(int) BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int Main.periodicIdiom(int) BCE (after)
  /// CHECK-NOT: BoundsCheck
  /// CHECK-NOT: Deoptimize
  private static int periodicIdiom(int tc) {
    int[] x = { 1, 3 };
    // Loop with periodic sequence (0, 1).
    int k = 0;
    int result = 0;
    for (int i = 0; i < tc; i++) {
      result += x[k];
      k = 1 - k;
    }
    return result;
  }

  /// CHECK-START: int Main.periodicSequence2(int) BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int Main.periodicSequence2(int) BCE (after)
  /// CHECK-NOT: BoundsCheck
  /// CHECK-NOT: Deoptimize
  private static int periodicSequence2(int tc) {
    int[] x = { 1, 3 };
    // Loop with periodic sequence (0, 1).
    int k = 0;
    int l = 1;
    int result = 0;
    for (int i = 0; i < tc; i++) {
      result += x[k];
      int t = l;
      l = k;
      k = t;
    }
    return result;
  }

  /// CHECK-START: int Main.periodicSequence4(int) BCE (before)
  /// CHECK-DAG: BoundsCheck
  /// CHECK-DAG: BoundsCheck
  /// CHECK-DAG: BoundsCheck
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int Main.periodicSequence4(int) BCE (after)
  /// CHECK-NOT: BoundsCheck
  /// CHECK-NOT: Deoptimize
  private static int periodicSequence4(int tc) {
    int[] x = { 1, 3, 5, 7 };
    // Loop with periodic sequence (0, 1, 2, 3).
    int k = 0;
    int l = 1;
    int m = 2;
    int n = 3;
    int result = 0;
    for (int i = 0; i < tc; i++) {
      result += x[k] + x[l] + x[m] + x[n];  // all used at once
      int t = n;
      n = k;
      k = l;
      l = m;
      m = t;
    }
    return result;
  }

  /// CHECK-START: int Main.periodicXorSequence(int) BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int Main.periodicXorSequence(int) BCE (after)
  /// CHECK-NOT: BoundsCheck
  /// CHECK-NOT: Deoptimize
  private static int periodicXorSequence(int tc) {
    int[] x = { 1, 3 };
    // Loop with periodic sequence (0, 1).
    int k = 0;
    int result = 0;
    for (int i = 0; i < tc; i++) {
      result += x[k];
      k ^= 1;
    }
    return result;
  }

  /// CHECK-START: int Main.justRightUp1() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int Main.justRightUp1() BCE (after)
  /// CHECK-NOT: BoundsCheck
  /// CHECK-NOT: Deoptimize
  private static int justRightUp1() {
    int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int result = 0;
    for (int i = Integer.MAX_VALUE - 10, k = 0; i < Integer.MAX_VALUE; i++) {
      result += x[k++];
    }
    return result;
  }

  /// CHECK-START: int Main.justRightUp2() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int Main.justRightUp2() BCE (after)
  /// CHECK-NOT: BoundsCheck
  /// CHECK-NOT: Deoptimize
  private static int justRightUp2() {
    int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int result = 0;
    for (int i = Integer.MAX_VALUE - 10; i < Integer.MAX_VALUE; i++) {
      result += x[i - Integer.MAX_VALUE + 10];
    }
    return result;
  }

  /// CHECK-START: int Main.justRightUp3() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int Main.justRightUp3() BCE (after)
  /// CHECK-NOT: BoundsCheck
  /// CHECK-NOT: Deoptimize
  private static int justRightUp3() {
    int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int result = 0;
    for (int i = Integer.MAX_VALUE - 10, k = 0; i <= Integer.MAX_VALUE - 1; i++) {
      result += x[k++];
    }
    return result;
  }

  /// CHECK-START: int Main.justOOBUp() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int Main.justOOBUp() BCE (after)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int Main.justOOBUp() BCE (after)
  /// CHECK-NOT: Deoptimize
  private static int justOOBUp() {
    int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int result = 0;
    // Infinite loop!
    for (int i = Integer.MAX_VALUE - 9, k = 0; i <= Integer.MAX_VALUE; i++) {
      result += x[k++];
    }
    return result;
  }

  /// CHECK-START: int Main.justRightDown1() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int Main.justRightDown1() BCE (after)
  /// CHECK-NOT: BoundsCheck
  /// CHECK-NOT: Deoptimize
  private static int justRightDown1() {
    int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int result = 0;
    for (int i = Integer.MIN_VALUE + 10, k = 0; i > Integer.MIN_VALUE; i--) {
      result += x[k++];
    }
    return result;
  }

  /// CHECK-START: int Main.justRightDown2() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int Main.justRightDown2() BCE (after)
  /// CHECK-NOT: BoundsCheck
  /// CHECK-NOT: Deoptimize
  private static int justRightDown2() {
    int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int result = 0;
    for (int i = Integer.MIN_VALUE + 10; i > Integer.MIN_VALUE; i--) {
      result += x[Integer.MAX_VALUE + i];
    }
    return result;
  }

  /// CHECK-START: int Main.justRightDown3() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int Main.justRightDown3() BCE (after)
  /// CHECK-NOT: BoundsCheck
  /// CHECK-NOT: Deoptimize
  private static int justRightDown3() {
    int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int result = 0;
    for (int i = Integer.MIN_VALUE + 10, k = 0; i >= Integer.MIN_VALUE + 1; i--) {
      result += x[k++];
    }
    return result;
  }

  /// CHECK-START: int Main.justOOBDown() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int Main.justOOBDown() BCE (after)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int Main.justOOBDown() BCE (after)
  /// CHECK-NOT: Deoptimize
  private static int justOOBDown() {
    int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int result = 0;
    // Infinite loop!
    for (int i = Integer.MIN_VALUE + 9, k = 0; i >= Integer.MIN_VALUE; i--) {
      result += x[k++];
    }
    return result;
  }

  /// CHECK-START: void Main.lowerOOB(int[]) BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.lowerOOB(int[]) BCE (after)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.lowerOOB(int[]) BCE (after)
  /// CHECK-NOT: Deoptimize
  private static void lowerOOB(int[] x) {
    // OOB!
    for (int i = -1; i < x.length; i++) {
      sResult += x[i];
    }
  }

  /// CHECK-START: void Main.upperOOB(int[]) BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.upperOOB(int[]) BCE (after)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.upperOOB(int[]) BCE (after)
  /// CHECK-NOT: Deoptimize
  private static void upperOOB(int[] x) {
    // OOB!
    for (int i = 0; i <= x.length; i++) {
      sResult += x[i];
    }
  }

  /// CHECK-START: void Main.doWhileUpOOB() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.doWhileUpOOB() BCE (after)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.doWhileUpOOB() BCE (after)
  /// CHECK-NOT: Deoptimize
  private static void doWhileUpOOB() {
    int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int i = 0;
    // OOB!
    do {
      sResult += x[i++];
    } while (i <= x.length);
  }

  /// CHECK-START: void Main.doWhileDownOOB() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.doWhileDownOOB() BCE (after)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.doWhileDownOOB() BCE (after)
  /// CHECK-NOT: Deoptimize
  private static void doWhileDownOOB() {
    int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int i = x.length - 1;
    // OOB!
    do {
      sResult += x[i--];
    } while (-1 <= i);
  }

  /// CHECK-START: void Main.justRightTriangular1() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.justRightTriangular1() BCE (after)
  /// CHECK-NOT: BoundsCheck
  /// CHECK-NOT: Deoptimize
  private static void justRightTriangular1() {
    int[] a = { 1 } ;
    for (int i = Integer.MIN_VALUE + 5; i <= Integer.MIN_VALUE + 10; i++) {
      for (int j = Integer.MIN_VALUE + 4; j < i - 5; j++) {
        sResult += a[j - (Integer.MIN_VALUE + 4)];
      }
    }
  }

  /// CHECK-START: void Main.justRightTriangular2() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.justRightTriangular2() BCE (after)
  /// CHECK-NOT: BoundsCheck
  /// CHECK-NOT: Deoptimize
  private static void justRightTriangular2() {
    int[] a = { 1 } ;
    for (int i = Integer.MIN_VALUE + 5; i <= 10; i++) {
      for (int j = 4; j < i - 5; j++) {
        sResult += a[j - 4];
      }
    }
  }

  /// CHECK-START: void Main.justOOBTriangular() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.justOOBTriangular() BCE (after)
  /// CHECK-DAG: Deoptimize
  //
  /// CHECK-START: void Main.justOOBTriangular() BCE (after)
  /// CHECK-NOT: BoundsCheck
  private static void justOOBTriangular() {
    int[] a = { 1 } ;
    for (int i = Integer.MIN_VALUE + 4; i <= 10; i++) {
      for (int j = 4; j < i - 5; j++) {
        sResult += a[j - 4];
      }
    }
  }

  /// CHECK-START: void Main.hiddenOOB1(int) BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.hiddenOOB1(int) BCE (after)
  /// CHECK-DAG: Deoptimize
  //
  /// CHECK-START: void Main.hiddenOOB1(int) BCE (after)
  /// CHECK-NOT: BoundsCheck
  private static void hiddenOOB1(int lo) {
    int[] a = { 1 } ;
    for (int i = lo; i <= 10; i++) {
      // Dangerous loop where careless static range analysis would yield strict upper bound
      // on index j of 5. When, for instance, lo and thus i = -2147483648, the upper bound
      // becomes really positive due to arithmetic wrap-around, causing OOB.
      for (int j = 4; j < i - 5; j++) {
        sResult += a[j - 4];
      }
    }
  }

  /// CHECK-START: void Main.hiddenOOB2(int) BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.hiddenOOB2(int) BCE (after)
  /// CHECK-DAG: Deoptimize
  //
  /// CHECK-START: void Main.hiddenOOB2(int) BCE (after)
  /// CHECK-NOT: BoundsCheck
  private static void hiddenOOB2(int hi) {
    int[] a = { 1 } ;
    for (int i = 0; i < hi; i++) {
      // Dangerous loop where careless static range analysis would yield strict lower bound
      // on index j of 5. When, for instance, hi and thus i = 2147483647, the upper bound
      // becomes really negative due to arithmetic wrap-around, causing OOB.
      for (int j = 6; j > i + 5; j--) {
        sResult += a[j - 6];
      }
    }
  }

  /// CHECK-START: void Main.hiddenOOB3(int) BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.hiddenOOB3(int) BCE (after)
  /// CHECK-DAG: Deoptimize
  //
  /// CHECK-START: void Main.hiddenOOB3(int) BCE (after)
  /// CHECK-NOT: BoundsCheck
  private static void hiddenOOB3(int hi) {
    int[] a = { 11 } ;
    for (int i = -1; i <= hi; i++) {
      // Dangerous loop where careless static range analysis would yield strict lower bound
      // on index j of 0. For large i, the initial value of j becomes really negative due
      // to arithmetic wrap-around, causing OOB.
      for (int j = i + 1; j < 1; j++) {
        sResult += a[j];
      }
    }
  }

  /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after)
  /// CHECK-NOT: Deoptimize
  private static void hiddenInfiniteOOB() {
    int[] a = { 11 } ;
    for (int i = -1; i <= 0; i++) {
      // Dangerous loop where careless static range analysis would yield a safe upper bound
      // of -3. In reality, due to arithmetic wrap-around (when i = -1, j <= 2147483647;
      // whereas when i = 0, j <= -3), this is an infinite loop that goes OOB.
      for (int j = -3; j <= 2147483646 * i - 3; j++) {
        sResult += a[j + 3];
      }
    }
  }

  /// CHECK-START: void Main.hiddenFiniteOOB() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after)
  /// CHECK-DAG: Deoptimize
  //
  /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after)
  /// CHECK-NOT: BoundsCheck
  private static void hiddenFiniteOOB() {
    int[] a = { 111 } ;
    for (int i = -1; i <= 0; i++) {
      // Dangerous loop similar as above where the loop is now finite, but the
      // loop still goes out of bounds for i = -1 due to the large upper bound.
      for (int j = -4; j < 2147483646 * i - 3; j++) {
        sResult += a[j + 4];
      }
    }
  }

  /// CHECK-START: void Main.inductionOOB(int[]) BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.inductionOOB(int[]) BCE (after)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.inductionOOB(int[]) BCE (after)
  /// CHECK-NOT: Deoptimize
  private static void inductionOOB(int[] a) {
    // Careless range analysis would remove the bounds check.
    // However, the narrower induction b wraps around arithmetically
    // before it reaches the end of arrays longer than 127.
    byte b = 0;
    for (int i = 0; i < a.length; i++) {
      a[b++] = i;
    }
  }

  /// CHECK-START: void Main.controlOOB(int[]) BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.controlOOB(int[]) BCE (after)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.controlOOB(int[]) BCE (after)
  /// CHECK-NOT: Deoptimize
  private static void controlOOB(int[] a) {
    // As above, but now the loop control also wraps around.
    for (byte i = 0; i < a.length; i++) {
      a[i] = -i;
    }
  }

  /// CHECK-START: void Main.conversionOOB(int[]) BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.conversionOOB(int[]) BCE (after)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: void Main.conversionOOB(int[]) BCE (after)
  /// CHECK-NOT: Deoptimize
  private static void conversionOOB(int[] a) {
    // As above, but with wrap around caused by an explicit conversion.
    for (int i = 0; i < a.length; ) {
      a[i] = i;
      i = (byte) (i + 1);
    }
  }

  /// CHECK-START: int Main.doNotHoist(int[]) BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int Main.doNotHoist(int[]) BCE (after)
  /// CHECK-NOT: BoundsCheck
  public static int doNotHoist(int[] a) {
     int n = a.length;
     int x = 0;
     // BCE applies, but hoisting would crash the loop.
     for (int i = -10000; i < 10000; i++) {
       for (int j = 0; j <= 1; j++) {
         if (0 <= i && i < n)
           x += a[i];
       }
    }
    return x;
  }


  /// CHECK-START: int[] Main.add() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int[] Main.add() BCE (after)
  /// CHECK-NOT: BoundsCheck
  /// CHECK-NOT: Deoptimize
  private static int[] add() {
    int[] a = new int[10];
    for (int i = 0; i <= 3; i++) {
      for (int j = 0; j <= 6; j++) {
        a[i + j] += 1;
      }
    }
    return a;
  }

  /// CHECK-START: int[] Main.multiply1() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int[] Main.multiply1() BCE (after)
  /// CHECK-NOT: BoundsCheck
  /// CHECK-NOT: Deoptimize
  private static int[] multiply1() {
    int[] a = new int[10];
    try {
      for (int i = 0; i <= 3; i++) {
        for (int j = 0; j <= 3; j++) {
          // Range [0,9]: safe.
          a[i * j] += 1;
        }
      }
    } catch (Exception e) {
      a[0] += 1000;
    }
    return a;
  }

  /// CHECK-START: int[] Main.multiply2() BCE (before)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int[] Main.multiply2() BCE (after)
  /// CHECK-DAG: BoundsCheck
  //
  /// CHECK-START: int[] Main.multiply2() BCE (after)
  /// CHECK-NOT: Deoptimize
  static int[] multiply2() {
    int[] a = new int[10];
    try {
      for (int i = -3; i <= 3; i++) {
        for (int j = -3; j <= 3; j++) {
          // Range [-9,9]: unsafe.
          a[i * j] += 1;
        }
      }
    } catch (Exception e) {
      a[0] += 1000;
    }
    return a;
  }

  /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (before)
  /// CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
  /// CHECK-DAG: NullCheck   loop:<<Loop>>
  /// CHECK-DAG: ArrayLength loop:<<Loop>>
  /// CHECK-DAG: BoundsCheck loop:<<Loop>>
  //
  /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after)
  /// CHECK-DAG: ArrayGet    loop:{{B\d+}}
  /// CHECK-DAG: Deoptimize  loop:none
  //
  /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after)
  /// CHECK-NOT: NullCheck   loop:{{B\d+}}
  /// CHECK-NOT: ArrayLength loop:{{B\d+}}
  /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
  private static int linearDynamicBCE1(int[] x, int lo, int hi) {
    int result = 0;
    for (int i = lo; i < hi; i++) {
      sResult += x[i];
    }
    return result;
  }

  /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (before)
  /// CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
  /// CHECK-DAG: NullCheck   loop:<<Loop>>
  /// CHECK-DAG: ArrayLength loop:<<Loop>>
  /// CHECK-DAG: BoundsCheck loop:<<Loop>>
  //
  /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after)
  /// CHECK-DAG: ArrayGet    loop:{{B\d+}}
  /// CHECK-DAG: Deoptimize  loop:none
  //
  /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after)
  /// CHECK-NOT: NullCheck   loop:{{B\d+}}
  /// CHECK-NOT: ArrayLength loop:{{B\d+}}
  /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
  private static int linearDynamicBCE2(int[] x, int lo, int hi, int offset) {
    int result = 0;
    for (int i = lo; i < hi; i++) {
      sResult += x[offset + i];
    }
    return result;
  }

  /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (before)
  /// CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
  /// CHECK-DAG: NullCheck   loop:<<Loop>>
  /// CHECK-DAG: ArrayLength loop:<<Loop>>
  /// CHECK-DAG: BoundsCheck loop:<<Loop>>
  //
  /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after)
  /// CHECK-DAG: ArrayGet    loop:{{B\d+}}
  /// CHECK-DAG: Deoptimize  loop:none
  //
  /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after)
  /// CHECK-NOT: NullCheck   loop:{{B\d+}}
  /// CHECK-NOT: ArrayLength loop:{{B\d+}}
  /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
  private static int wrapAroundDynamicBCE(int[] x) {
    int w = 9;
    int result = 0;
    for (int i = 0; i < 10; i++) {
      result += x[w];
      w = i;
    }
    return result;
  }

  /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (before)
  /// CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
  /// CHECK-DAG: NullCheck   loop:<<Loop>>
  /// CHECK-DAG: ArrayLength loop:<<Loop>>
  /// CHECK-DAG: BoundsCheck loop:<<Loop>>
  //
  /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after)
  /// CHECK-DAG: ArrayGet    loop:{{B\d+}}
  /// CHECK-DAG: Deoptimize  loop:none
  //
  /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after)
  /// CHECK-NOT: NullCheck   loop:{{B\d+}}
  /// CHECK-NOT: ArrayLength loop:{{B\d+}}
  /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
  private static int periodicDynamicBCE(int[] x) {
    int k = 0;
    int result = 0;
    for (int i = 0; i < 10; i++) {
      result += x[k];
      k = 1 - k;
    }
    return result;
  }

  /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before)
  /// CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
  /// CHECK-DAG: NullCheck   loop:<<Loop>>
  /// CHECK-DAG: ArrayLength loop:<<Loop>>
  /// CHECK-DAG: BoundsCheck loop:<<Loop>>
  //
  /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
  /// CHECK-DAG: ArrayGet    loop:{{B\d+}}
  /// CHECK-DAG: Deoptimize  loop:none
  //
  /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
  /// CHECK-NOT: NullCheck   loop:{{B\d+}}
  /// CHECK-NOT: ArrayLength loop:{{B\d+}}
  /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
  static int dynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) {
    // This loop could be infinite for hi = max int. Since i is also used
    // as subscript, however, dynamic bce can proceed.
    int result = 0;
    for (int i = lo; i <= hi; i++) {
      result += x[i];
    }
    return result;
  }

  /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before)
  /// CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
  /// CHECK-DAG: BoundsCheck loop:<<Loop>>
  //
  /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
  /// CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
  /// CHECK-DAG: BoundsCheck loop:<<Loop>>
  //
  /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
  /// CHECK-NOT: Deoptimize
  static int noDynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) {
    // As above, but now the index is not used as subscript,
    // and dynamic bce is not applied.
    int result = 0;
    for (int k = 0, i = lo; i <= hi; i++) {
      result += x[k++];
    }
    return result;
  }

  /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (before)
  /// CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
  /// CHECK-DAG: BoundsCheck loop:<<Loop>>
  //
  /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after)
  /// CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
  /// CHECK-DAG: BoundsCheck loop:<<Loop>>
  //
  /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after)
  /// CHECK-NOT: Deoptimize
  static int noDynamicBCEMixedInductionTypes(int[] x, long lo, long hi) {
    int result = 0;
    // Mix of int and long induction.
    int k = 0;
    for (long i = lo; i < hi; i++) {
      result += x[k++];
    }
    return result;
  }

  /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (before)
  /// CHECK-DAG: BoundsCheck loop:<<InnerLoop:B\d+>>
  /// CHECK-DAG: ArrayGet    loop:<<InnerLoop>>
  /// CHECK-DAG: If          loop:<<InnerLoop>>
  /// CHECK-DAG: If          loop:<<OuterLoop:B\d+>>
  /// CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>"
  //
  /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after)
  /// CHECK-DAG: ArrayGet   loop:<<InnerLoop:B\d+>>
  /// CHECK-DAG: Deoptimize loop:<<OuterLoop:B\d+>>
  /// CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>"
  //
  /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after)
  /// CHECK-NOT: BoundsCheck
  //
  //  No additional top tests were introduced.
  /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after)
  /// CHECK-DAG: If
  /// CHECK-DAG: If
  /// CHECK-NOT: If
  static int dynamicBCEConstantRange(int[] x) {
    int result = 0;
    for (int i = 2; i <= 6; i++) {
      // Range analysis sees that innermost loop is finite and always taken.
      for (int j = i - 2; j <= i + 2; j++) {
        result += x[j];
      }
    }
    return result;
  }

  /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (before)
  /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop:B\d+>>
  /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
  /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
  //
  /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after)
  //  Order matters:
  /// CHECK:              Deoptimize loop:<<Loop:B\d+>>
  /// CHECK-NOT:          Goto       loop:<<Loop>>
  /// CHECK-DAG: {{l\d+}} ArrayGet   loop:<<Loop>>
  /// CHECK-DAG: {{l\d+}} ArrayGet   loop:<<Loop>>
  /// CHECK-DAG: {{l\d+}} ArrayGet   loop:<<Loop>>
  /// CHECK:              Goto       loop:<<Loop>>
  //
  /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after)
  /// CHECK-DAG: Deoptimize loop:none
  static int dynamicBCEAndConstantIndices(int[] x, int[][] a, int lo, int hi) {
    // Deliberately test array length on a before the loop so that only bounds checks
    // on constant subscripts remain, making them a viable candidate for hoisting.
    if (a.length == 0) {
      return -1;
    }
    // Loop that allows BCE on x[i].
    int result = 0;
    for (int i = lo; i < hi; i++) {
      result += x[i];
      if ((i % 10) != 0) {
        // None of the subscripts inside a conditional are removed by dynamic bce,
        // making them a candidate for deoptimization based on constant indices.
        // Compiler should ensure the array loads are not subsequently hoisted
        // "above" the deoptimization "barrier" on the bounds.
        a[1][i] = 1;
        a[2][i] = 2;
        a[99][i] = 3;
      }
    }
    return result;
  }

  /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (before)
  /// CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
  /// CHECK-DAG: ArrayGet    loop:<<Loop>>
  /// CHECK-DAG: ArrayGet    loop:<<Loop>>
  /// CHECK-DAG: ArrayGet    loop:<<Loop>>
  /// CHECK-DAG: ArrayGet    loop:<<Loop>>
  /// CHECK-DAG: ArrayGet    loop:<<Loop>>
  /// CHECK-DAG: ArrayGet    loop:<<Loop>>
  /// CHECK-DAG: ArrayGet    loop:<<Loop>>
  /// CHECK-DAG: ArrayGet    loop:<<Loop>>
  //  For brevity, just test occurrence of at least one of each in the loop:
  /// CHECK-DAG: NullCheck   loop:<<Loop>>
  /// CHECK-DAG: ArrayLength loop:<<Loop>>
  /// CHECK-DAG: BoundsCheck loop:<<Loop>>
  //
  /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
  /// CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
  /// CHECK-NOT: ArrayGet    loop:<<Loop>>
  //
  /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
  /// CHECK-NOT: NullCheck   loop:{{B\d+}}
  /// CHECK-NOT: ArrayLength loop:{{B\d+}}
  /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
  //
  /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
  /// CHECK-DAG: Deoptimize  loop:none
  static int dynamicBCEAndConstantIndicesAllPrimTypes(int[] q,
                                                      boolean[] r,
                                                      byte[] s,
                                                      char[] t,
                                                      short[] u,
                                                      int[] v,
                                                      long[] w,
                                                      float[] x,
                                                      double[] y, int lo, int hi) {
    int result = 0;
    for (int i = lo; i < hi; i++) {
      // All constant index array references can be hoisted out of the loop during BCE on q[i].
      result += q[i] + (r[0] ? 1 : 0) + (int) s[0] + (int) t[0] + (int) u[0] + (int) v[0] +
                                        (int) w[0] + (int) x[0] + (int) y[0];
    }
    return result;
  }

  /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (before)
  /// CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
  /// CHECK-DAG: NullCheck   loop:<<Loop>>
  /// CHECK-DAG: ArrayLength loop:<<Loop>>
  /// CHECK-DAG: BoundsCheck loop:<<Loop>>
  /// CHECK-DAG: ArrayGet    loop:<<Loop>>
  /// CHECK-DAG: NullCheck   loop:<<Loop>>
  /// CHECK-DAG: ArrayLength loop:<<Loop>>
  /// CHECK-DAG: BoundsCheck loop:<<Loop>>
  //
  /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after)
  /// CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
  /// CHECK-DAG: Deoptimize  loop:none
  //
  /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after)
  /// CHECK-NOT: ArrayLength loop:{{B\d+}}
  /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
  static int dynamicBCEAndConstantIndexRefType(int[] q, Integer[] z, int lo, int hi) {
    int result = 0;
    for (int i = lo; i < hi; i++) {
      // Similar to above, but now implicit call to intValue() may prevent hoisting
      // z[0] itself during BCE on q[i]. Therefore, we just check BCE on q[i].
      result += q[i] + z[0];
    }
    return result;
  }

  static int shortIndex(int[] a) {
    int r = 0;
    // Make sure short/int conversions compiles well (b/32193474).
    // TODO: investigate type implications and whether we can use
    //       constant range to apply dyn BCE on all subscripts.
    for (short i = 1; i < 10; i++) {
      int ki = i - 1;
      r += a[ki] + a[i];
    }
    return r;
  }

  //
  // Verifier.
  //

  public static void main(String[] args) {
    // Set to run expensive tests for correctness too.
    boolean HEAVY = false;

    int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    int[] a200 = new int[200];

    // Sorting.
    int[] sort = { 5, 4, 1, 9, 10, 2, 7, 6, 3, 8 };
    bubble(sort);
    for (int i = 0; i < 10; i++) {
      expectEquals(sort[i], x[i]);
    }

    // Periodic adds (1, 3), one at the time.
    expectEquals(0, periodicIdiom(-1));
    for (int tc = 0; tc < 32; tc++) {
      int expected = (tc >> 1) << 2;
      if ((tc & 1) != 0) {
        expected += 1;
      }
      expectEquals(expected, periodicIdiom(tc));
    }

    // Periodic adds (1, 3), one at the time.
    expectEquals(0, periodicSequence2(-1));
    for (int tc = 0; tc < 32; tc++) {
      int expected = (tc >> 1) << 2;
      if ((tc & 1) != 0) {
        expected += 1;
      }
      expectEquals(expected, periodicSequence2(tc));
    }

    // Periodic adds (1, 3, 5, 7), all at once.
    expectEquals(0, periodicSequence4(-1));
    for (int tc = 0; tc < 32; tc++) {
      expectEquals(tc * 16, periodicSequence4(tc));
    }

    // Periodic adds (1, 3), one at the time.
    expectEquals(0, periodicXorSequence(-1));
    for (int tc = 0; tc < 32; tc++) {
      int expected = (tc >> 1) << 2;
      if ((tc & 1) != 0) {
        expected += 1;
      }
      expectEquals(expected, periodicXorSequence(tc));
    }

    // Large bounds.
    expectEquals(55, justRightUp1());
    expectEquals(55, justRightUp2());
    expectEquals(55, justRightUp3());
    expectEquals(55, justRightDown1());
    expectEquals(55, justRightDown2());
    expectEquals(55, justRightDown3());

    // Large bounds OOB.
    sResult = 0;
    try {
      justOOBUp();
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult = 1;
    }
    expectEquals(1, sResult);
    sResult = 0;
    try {
      justOOBDown();
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult = 1;
    }
    expectEquals(1, sResult);

    // Lower bound goes OOB.
    sResult = 0;
    try {
      lowerOOB(x);
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1000, sResult);

    // Upper bound goes OOB.
    sResult = 0;
    try {
      upperOOB(x);
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1055, sResult);

    // Do while up goes OOB.
    sResult = 0;
    try {
      doWhileUpOOB();
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1055, sResult);

    // Do while down goes OOB.
    sResult = 0;
    try {
      doWhileDownOOB();
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1055, sResult);

    // Triangular.
    sResult = 0;
    justRightTriangular1();
    expectEquals(1, sResult);
    if (HEAVY) {
      sResult = 0;
      justRightTriangular2();
      expectEquals(1, sResult);
    }
    sResult = 0;
    try {
      justOOBTriangular();
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1001, sResult);

    // Hidden OOB.
    sResult = 0;
    try {
      hiddenOOB1(10);  // no OOB
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1, sResult);
    sResult = 0;
    try {
      hiddenOOB1(-2147483648);  // OOB
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1001, sResult);
    sResult = 0;
    try {
      hiddenOOB2(1);  // no OOB
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1, sResult);
    sResult = 0;
    try {
      hiddenOOB3(-1);  // no OOB
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(11, sResult);

    // Expensive hidden OOB test.
    if (HEAVY) {
      sResult = 0;
      try {
        hiddenOOB2(2147483647);  // OOB
      } catch (ArrayIndexOutOfBoundsException e) {
        sResult += 1000;
      }
      expectEquals(1002, sResult);
      sResult = 0;
      try {
        hiddenOOB3(2147483647);  // OOB
      } catch (ArrayIndexOutOfBoundsException e) {
        sResult += 1000;
      }
      expectEquals(1011, sResult);
    }

    // More hidden OOB.
    sResult = 0;
    try {
      hiddenInfiniteOOB();
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1011, sResult);
    sResult = 0;
    try {
      hiddenFiniteOOB();
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1111, sResult);
    sResult = 0;
    try {
      inductionOOB(a200);
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1000, sResult);
    for (int i = 0; i < 200; i++) {
      expectEquals(i < 128 ? i : 0, a200[i]);
    }
    sResult = 0;
    try {
      controlOOB(a200);
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1000, sResult);
    for (int i = 0; i < 200; i++) {
      expectEquals(i < 128 ? -i : 0, a200[i]);
    }
    sResult = 0;
    try {
      conversionOOB(a200);
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1000, sResult);
    for (int i = 0; i < 200; i++) {
      expectEquals(i < 128 ? i : 0, a200[i]);
    }

    // No hoisting after BCE.
    expectEquals(110, doNotHoist(x));

    // Addition.
    {
      int[] e1 ={ 1, 2, 3, 4, 4, 4, 4, 3, 2, 1 };
      int[] a1 = add();
      for (int i = 0; i < 10; i++) {
        expectEquals(a1[i], e1[i]);
      }
    }

    // Multiplication.
    {
      int[] e1 = { 7, 1, 2, 2, 1, 0, 2, 0, 0, 1 };
      int[] a1 = multiply1();
      for (int i = 0; i < 10; i++) {
        expectEquals(a1[i], e1[i]);
      }
      int[] e2 = { 1001, 0, 0, 1, 0, 0, 1, 0, 0, 1 };
      int[] a2 = multiply2();
      for (int i = 0; i < 10; i++) {
        expectEquals(a2[i], e2[i]);
      }
    }

    // Dynamic BCE.
    sResult = 0;
    try {
      linearDynamicBCE1(x, -1, x.length);
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1000, sResult);
    sResult = 0;
    linearDynamicBCE1(x, 0, x.length);
    expectEquals(55, sResult);
    sResult = 0;
    try {
      linearDynamicBCE1(x, 0, x.length + 1);
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1055, sResult);

    // Dynamic BCE with offset.
    sResult = 0;
    try {
      linearDynamicBCE2(x, 0, x.length, -1);
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1000, sResult);
    sResult = 0;
    linearDynamicBCE2(x, 0, x.length, 0);
    expectEquals(55, sResult);
    sResult = 0;
    try {
      linearDynamicBCE2(x, 0, x.length, 1);
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult += 1000;
    }
    expectEquals(1054, sResult);

    // Dynamic BCE candidates.
    expectEquals(55, wrapAroundDynamicBCE(x));
    expectEquals(15, periodicDynamicBCE(x));
    expectEquals(55, dynamicBCEPossiblyInfiniteLoop(x, 0, 9));
    expectEquals(55, noDynamicBCEPossiblyInfiniteLoop(x, 0, 9));
    expectEquals(55, noDynamicBCEMixedInductionTypes(x, 0, 10));
    expectEquals(125, dynamicBCEConstantRange(x));

    // Dynamic BCE combined with constant indices.
    int[][] a;
    a = new int[0][0];
    expectEquals(-1, dynamicBCEAndConstantIndices(x, a, 0, 10));
    a = new int[100][10];
    expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10));
    for (int i = 0; i < 10; i++) {
      expectEquals((i % 10) != 0 ? 1 : 0, a[1][i]);
      expectEquals((i % 10) != 0 ? 2 : 0, a[2][i]);
      expectEquals((i % 10) != 0 ? 3 : 0, a[99][i]);
    }
    a = new int[3][10];
    sResult = 0;
    try {
      expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10));
    } catch (ArrayIndexOutOfBoundsException e) {
      sResult = 1;
    }
    expectEquals(1, sResult);
    expectEquals(a[1][1], 1);
    expectEquals(a[2][1], 2);

    // Dynamic BCE combined with constant indices of all types.
    boolean[] x1 = { true };
    byte[] x2 = { 2 };
    char[] x3 = { 3 };
    short[] x4 = { 4 };
    int[] x5 = { 5 };
    long[] x6 = { 6 };
    float[] x7 = { 7 };
    double[] x8 = { 8 };
    expectEquals(415,
        dynamicBCEAndConstantIndicesAllPrimTypes(x, x1, x2, x3, x4, x5, x6, x7, x8, 0, 10));
    Integer[] x9 = { 9 };
    expectEquals(145, dynamicBCEAndConstantIndexRefType(x, x9, 0, 10));

    expectEquals(99, shortIndex(x));

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

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