/* * 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:<> /// CHECK-DAG: NullCheck loop:<> /// CHECK-DAG: ArrayLength loop:<> /// CHECK-DAG: BoundsCheck 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:<> /// CHECK-DAG: NullCheck loop:<> /// CHECK-DAG: ArrayLength loop:<> /// CHECK-DAG: BoundsCheck 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:<> /// CHECK-DAG: NullCheck loop:<> /// CHECK-DAG: ArrayLength loop:<> /// CHECK-DAG: BoundsCheck 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:<> /// CHECK-DAG: NullCheck loop:<> /// CHECK-DAG: ArrayLength loop:<> /// CHECK-DAG: BoundsCheck 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:<> /// CHECK-DAG: NullCheck loop:<> /// CHECK-DAG: ArrayLength loop:<> /// CHECK-DAG: BoundsCheck 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:<> /// CHECK-DAG: BoundsCheck loop:<> // /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) /// CHECK-DAG: ArrayGet loop:<> /// CHECK-DAG: BoundsCheck 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:<> /// CHECK-DAG: BoundsCheck loop:<> // /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) /// CHECK-DAG: ArrayGet loop:<> /// CHECK-DAG: BoundsCheck 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:<> /// CHECK-DAG: ArrayGet loop:<> /// CHECK-DAG: If loop:<> /// CHECK-DAG: If loop:<> /// CHECK-EVAL: "<>" != "<>" // /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) /// CHECK-DAG: ArrayGet loop:<> /// CHECK-DAG: Deoptimize loop:<> /// CHECK-EVAL: "<>" != "<>" // /// 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:<> /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> // /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) // Order matters: /// CHECK: Deoptimize loop:<> // CHECK-NOT: Goto loop:<> /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> /// CHECK: Goto 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:<> /// CHECK-DAG: ArrayGet loop:<> /// CHECK-DAG: ArrayGet loop:<> /// CHECK-DAG: ArrayGet loop:<> /// CHECK-DAG: ArrayGet loop:<> /// CHECK-DAG: ArrayGet loop:<> /// CHECK-DAG: ArrayGet loop:<> /// CHECK-DAG: ArrayGet loop:<> /// CHECK-DAG: ArrayGet loop:<> // For brevity, just test occurrence of at least one of each in the loop: /// CHECK-DAG: NullCheck loop:<> /// CHECK-DAG: ArrayLength loop:<> /// CHECK-DAG: BoundsCheck loop:<> // /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) /// CHECK-DAG: ArrayGet loop:<> /// CHECK-NOT: ArrayGet 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:<> /// CHECK-DAG: NullCheck loop:<> /// CHECK-DAG: ArrayLength loop:<> /// CHECK-DAG: BoundsCheck loop:<> /// CHECK-DAG: ArrayGet loop:<> /// CHECK-DAG: NullCheck loop:<> /// CHECK-DAG: ArrayLength loop:<> /// CHECK-DAG: BoundsCheck loop:<> // /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) /// CHECK-DAG: ArrayGet loop:<> /// 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); } } }