| /* |
| * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* |
| * @test |
| * @bug 8292088 |
| * @requires vm.compiler2.enabled |
| * @summary Test that OuterStripMinedLoop and its CountedLoop are both removed after the removal of Opaque1 and 2 nodes |
| which allows the loop backedge to be optimized out. |
| * @run main/othervm -XX:LoopMaxUnroll=0 -Xcomp |
| * -XX:CompileCommand=compileonly,compiler.c2.TestRemoveSingleIterationLoop::test* |
| * -XX:CompileCommand=dontinline,compiler.c2.TestRemoveSingleIterationLoop::dontInline |
| * compiler.c2.TestRemoveSingleIterationLoop |
| * @run main/othervm -XX:LoopMaxUnroll=2 -Xcomp |
| * -XX:CompileCommand=compileonly,compiler.c2.TestRemoveSingleIterationLoop::test* |
| * -XX:CompileCommand=dontinline,compiler.c2.TestRemoveSingleIterationLoop::dontInline |
| * compiler.c2.TestRemoveSingleIterationLoop |
| */ |
| package compiler.c2; |
| |
| public class TestRemoveSingleIterationLoop { |
| static int N = 400; |
| static int x = 3; |
| static int y = 3; |
| static volatile int[] iArr = new int[N]; |
| |
| public static void main(String[] args) { |
| testKnownLimit(); |
| testUnknownLimit(); |
| testKnownLimit2(); |
| testFuzzer(); |
| } |
| |
| // Upper limit is known (i.e. type of iv phi for i is [-9999..499]). Unroll = 0 to trigger. |
| private static void testKnownLimit() { |
| int i = -10000; |
| int limit = 500; |
| |
| // These two loops are only required to make sure that we only know that 'i' is 5 after the second loop after CCP. |
| int a = 2; |
| for (; a < 4; a *= 2); |
| for (int b = 2; b < a; b++) { |
| i = 5; |
| } |
| |
| if (i < limit + 1) { // Required such that C2 knows that we are always entering the first loop. |
| // Loop L1 |
| for (; i < limit; i++) { |
| // IV_PHI_i = iv phi of i for this loop with type: |
| // - Before CPP: [-10000..499] |
| // - After CPP: [5..499] |
| y = 3; |
| } |
| |
| int j = 6; |
| // C2 parses the following loop as: |
| // Loop head |
| // body (where we do j--) |
| // Loop exit check where we already applied j--: j > i - 1 |
| while (j > i - 1) { |
| // IV_PHI_j = iv phi of j for this loop with type: |
| // - Before CPP: [-9998..7] |
| j--; |
| iArr[23] = 3; |
| // At this point i = IV_PHI_i + 1 because i was incremented once more before exiting loop L1. |
| // In PhaseIdealLoop::reorg_offsets(), we identify such direct (pre-incremented) usages of an iv phi and |
| // add an Opaque2 node to prevent loop-fallout uses. This lowers the register pressure. We replace the |
| // direct phi usage in CmpI (annotated with types before CCP): |
| // |
| // Phi Phi (IV_PHI_i) # [-10000..499] |
| // | | |
| // | AddI (+1) # [-9999..500] |
| // | | |
| // | Opaque2 # int |
| // | ====> | |
| // | AddI (-1) # int |
| // | | |
| // | CastII # [-10000..499] |
| // | | |
| // CmpI CmpI # j > i - 1 = IV_PHI_j - 1 > CastII (actually IV_PHI_i) = [-10000..5] > [-10000..499] |
| // |
| // |
| // After CCP, the type of the iv phi IV_PHI_i improves to [5..499] while the type of the CastII does not |
| // because the Opaque2 node blocks the type update. When removing the Opaque2 node, we find that the |
| // loop only runs for a single time and does not take the backedge: |
| // |
| // [-10000..5] > [5..499] is false |
| // |
| // However, we only remove Opaque2 nodes in macro expansion where we also adjust the strip mined loop: |
| // We copy the bool node from the CountedLoopEnd to the OuterStripMinedLoopEnd node and adjust the |
| // loop exit check of the CountedLoopEnd in such a way that C2 is not able to prove that the loop |
| // is only run once. But the OuterStripMinedLoop can now be removed due to the removed Opaque2 nodes |
| // and we end up with a CountedLoop node that is strip mined but has no OuterStripMined loop. This |
| // results in an assertion failure later when reshaping the graph. |
| } |
| } |
| } |
| |
| // Upper limit is not known (i.e. type of iv phi for i is [-9999..max-1]). Unroll = 0 to trigger. |
| private static void testUnknownLimit() { |
| int i = -10000; |
| int limit = x; |
| |
| // These two loops are required to make sure that we only know after CCP that 'i' is 1 after the second loop. |
| int a = 2; |
| for (; a < 4; a *= 2); |
| for (int b = 2; b < a; b++) { |
| i = 0; |
| } |
| if (i + 1 < limit) { |
| i++; |
| for (; i < limit; i++) { |
| y = 3; |
| } |
| int t = 2; |
| while (t > i - 1) { |
| t--; |
| iArr[23] = 3; |
| } |
| } |
| } |
| |
| // Upper limit is known. Unroll = 2 to trigger. |
| static int testKnownLimit2() { |
| int i = -10000, j; |
| int[][] iArr1 = new int[N][N]; |
| |
| // These two loops are required to make sure that we only know after CCP that 'i' is 1 after the second loop. |
| int a = 2; |
| for (; a < 4; a *= 2); |
| for (int b = 2; b < a; b++) { |
| i = 1; |
| } |
| |
| while (++i < 318) { |
| dontInline(); |
| } |
| |
| for (j = 6; j > i; j--) { |
| // Type of i before CCP: -9999..317 |
| // Type of i after CCP: 2..317 |
| iArr[1] = 25327; |
| } |
| |
| // This loop is required to trigger the assertion failure. |
| for (int y = 0; y < iArr1.length; y++) { |
| dontInline(iArr1[2]); |
| } |
| return i; |
| } |
| |
| // Reduced original fuzzer test. Unroll = 2 to trigger. |
| static int testFuzzer() { |
| int i = 1, j, iArr1[][] = new int[N][N]; |
| while (++i < 318) { |
| dontInline(); |
| for (j = 5; j > i; j--) { |
| iArr[1] = 25327; |
| } |
| } |
| |
| // Some more code that is required to trigger the assertion failure. |
| for (int y = 0; y < iArr1.length; y++) { |
| dontInline(iArr1[2]); |
| } |
| return i; |
| } |
| |
| static void dontInline() {} |
| |
| static void dontInline(int[] iArr) {} |
| } |