/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Tests for optimizations of break-loops, i.e. loops that break * out of a while-true loop when the end condition is satisfied. * In particular, the tests focus on break-loops that can be * rewritten into regular countable loops (this may improve certain * loops generated by the Kotlin compiler for inclusive ranges). */ public class Main { /// CHECK-START: int Main.breakLoop(int[]) induction_var_analysis (before) /// CHECK-DAG: <> ParameterValue loop:none /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> NullCheck [<>] loop:none /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> NotEqual [{{i\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: If [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none // /// CHECK-START: int Main.breakLoop(int[]) induction_var_analysis (after) /// CHECK-DAG: <> ParameterValue loop:none /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> NullCheck [<>] loop:none /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> LessThanOrEqual [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: If [<>] loop:<> outer_loop:none /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none // /// CHECK-START-ARM64: int Main.breakLoop(int[]) loop_optimization (after) /// CHECK-DAG: <> ParameterValue loop:none /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> IntConstant 4 loop:none /// CHECK-DAG: <> NullCheck [<>] loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: VecStore [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none static int breakLoop(int[] a) { int l = 0; int u = a.length - 1; int i = l; if (l <= u) { while (true) { a[i] = 1; if (i == u) break; i++; } } return i; } /// CHECK-START: int Main.breakLoopDown(int[]) induction_var_analysis (before) /// CHECK-DAG: <> ParameterValue loop:none /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant -1 loop:none /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> NullCheck [<>] loop:none /// CHECK-DAG: <> Phi [{{i\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: If [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none // /// CHECK-START: int Main.breakLoopDown(int[]) induction_var_analysis (after) /// CHECK-DAG: <> ParameterValue loop:none /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant -1 loop:none /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> NullCheck [<>] loop:none /// CHECK-DAG: <> Phi [{{i\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: If [<>] loop:<> outer_loop:none /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none static int breakLoopDown(int[] a) { int l = 0; int u = a.length - 1; int i = u; if (u >= l) { while (true) { a[i] = 2; if (i == l) break; i--; } } return i; } /// CHECK-START: int Main.breakLoopSafeConst(int[]) induction_var_analysis (before) /// CHECK-DAG: <> ParameterValue loop:none /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> IntConstant 3 loop:none /// CHECK-DAG: <> IntConstant 2147483631 loop:none /// CHECK-DAG: <> IntConstant 2147483646 loop:none /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> NullCheck [<>] loop:<> outer_loop:none /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: If [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none // /// CHECK-START: int Main.breakLoopSafeConst(int[]) induction_var_analysis (after) /// CHECK-DAG: <> ParameterValue loop:none /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> IntConstant 3 loop:none /// CHECK-DAG: <> IntConstant 2147483631 loop:none /// CHECK-DAG: <> IntConstant 2147483646 loop:none /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> LessThanOrEqual [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> NullCheck [<>] loop:<> outer_loop:none /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none // /// CHECK-START-ARM64: int Main.breakLoopSafeConst(int[]) loop_optimization (after) /// CHECK-DAG: <> ParameterValue loop:none /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> IntConstant 3 loop:none /// CHECK-DAG: <> IntConstant 4 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: VecStore [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none static int breakLoopSafeConst(int[] a) { int l = Integer.MAX_VALUE - 16; int u = Integer.MAX_VALUE - 1; int i = l; if (l <= u) { // will be removed by simplifier while (true) { a[i - l] = 3; if (i == u) break; i++; } } return i; } /// CHECK-START: int Main.breakLoopUnsafeConst(int[]) induction_var_analysis (before) /// CHECK-DAG: <> ParameterValue loop:none /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> IntConstant 4 loop:none /// CHECK-DAG: <> IntConstant 2147483632 loop:none /// CHECK-DAG: <> IntConstant 2147483647 loop:none /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> NullCheck [<>] loop:<> outer_loop:none /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: If [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none // /// CHECK-START: int Main.breakLoopUnsafeConst(int[]) induction_var_analysis (after) /// CHECK-DAG: NotEqual /// CHECK-NOT: LessThanOrEqual static int breakLoopUnsafeConst(int[] a) { int l = Integer.MAX_VALUE - 15; int u = Integer.MAX_VALUE; int i = l; if (l <= u) { // will be removed by simplifier while (true) { a[i - l] = 4; if (i == u) break; // rewriting exit not safe! i++; } } return i; } /// CHECK-START: int Main.breakLoopNastyPhi(int[]) induction_var_analysis (before) /// CHECK-DAG: <> ParameterValue loop:none /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> IntConstant 5 loop:none /// CHECK-DAG: <> IntConstant -123 loop:none /// CHECK-DAG: <> NullCheck [<>] loop:none /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> NotEqual [{{i\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: If [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Phi [<>,<>] loop:none /// CHECK-DAG: Return [<>] loop:none // /// CHECK-START: int Main.breakLoopNastyPhi(int[]) induction_var_analysis (after) /// CHECK-DAG: NotEqual /// CHECK-NOT: LessThanOrEqual static int breakLoopNastyPhi(int[] a) { int l = 0; int u = a.length - 1; int x = -123; if (l <= u) { int i = l; while (true) { a[i] = 5; if (i == u) break; x = i; i++; } } return x; // keep another phi live } /// CHECK-START: int Main.breakLoopReduction(int[]) induction_var_analysis (before) /// CHECK-DAG: <> ParameterValue loop:none /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> NullCheck [<>] loop:none /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> NotEqual [{{i\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: If [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Phi [<>,<>] loop:none /// CHECK-DAG: Return [<>] loop:none // /// CHECK-START: int Main.breakLoopReduction(int[]) induction_var_analysis (after) /// CHECK-DAG: <> ParameterValue loop:none /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> NullCheck [<>] loop:none /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> LessThanOrEqual [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: If [<>] loop:<> outer_loop:none /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Phi [<>,<>] loop:none /// CHECK-DAG: Return [<>] loop:none // /// CHECK-START-ARM64: int Main.breakLoopReduction(int[]) loop_optimization (after) /// CHECK-DAG: <> ParameterValue loop:none /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecAdd [<>,<>] loop:<> outer_loop:none static int breakLoopReduction(int[] a) { int l = 0; int u = a.length - 1; int x = 0; if (l <= u) { int i = l; while (true) { x += a[i]; if (i == u) break; i++; } } return x; } // // Test driver. // public static void main(String[] args) { int[] a = new int[100]; expectEquals(99, breakLoop(a)); for (int i = 0; i < a.length; i++) { expectEquals(1, a[i]); } expectEquals(0, breakLoopDown(a)); for (int i = 0; i < a.length; i++) { expectEquals(2, a[i]); } expectEquals(Integer.MAX_VALUE - 1, breakLoopSafeConst(a)); for (int i = 0; i < a.length; i++) { int e = i < 16 ? 3 : 2; expectEquals(e, a[i]); } expectEquals(Integer.MAX_VALUE, breakLoopUnsafeConst(a)); for (int i = 0; i < a.length; i++) { int e = i < 16 ? 4 : 2; expectEquals(e, a[i]); } expectEquals(98, breakLoopNastyPhi(a)); for (int i = 0; i < a.length; i++) { expectEquals(5, a[i]); } expectEquals(500, breakLoopReduction(a)); System.out.println("passed"); } private static void expectEquals(int expected, int result) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); } } }