content: /*
   * 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
    //  TODO: also CHECK-NOT: Deoptimize, see b/27151190
    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.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.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.
        // Dynamic BCE is feasible though, since it checks the range.
        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.
        // Dynamic BCE is feasible though, since it checks the range.
        for (int j = 6; j > i + 5; j--) {
          sResult += a[j - 6];
        }
      }
    }
  
    /// 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.
        // Dynamic BCE is feasible though, since it checks the range.
        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.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[0][i] = 1;
          a[1][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;
    }
  
    //
    // 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));
      }
  
      // Large bounds.
      expectEquals(55, justRightUp1());
      expectEquals(55, justRightUp2());
      expectEquals(55, justRightUp3());
      expectEquals(55, justRightDown1());
      expectEquals(55, justRightDown2());
      expectEquals(55, justRightDown3());
      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);
  
      // 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);
      if (HEAVY) {
        sResult = 0;
        try {
          hiddenOOB2(2147483647);  // OOB
        } catch (ArrayIndexOutOfBoundsException e) {
          sResult += 1000;
        }
        expectEquals(1002, sResult);
      }
      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]);
      }
  
      // 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[0][i]);
        expectEquals((i % 10) != 0 ? 2 : 0, a[1][i]);
        expectEquals((i % 10) != 0 ? 3 : 0, a[99][i]);
      }
      a = new int[2][10];
      sResult = 0;
      try {
        expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10));
      } catch (ArrayIndexOutOfBoundsException e) {
        sResult = 1;
      }
      expectEquals(1, sResult);
      expectEquals(a[0][1], 1);
      expectEquals(a[1][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));
  
      System.out.println("passed");
    }
  
    private static void expectEquals(int expected, int result) {
      if (expected != result) {
        throw new Error("Expected: " + expected + ", found: " + result);
      }
    }
  }
  