|
| 1 | +/* |
| 2 | + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | + * |
| 5 | + * This code is free software; you can redistribute it and/or modify it |
| 6 | + * under the terms of the GNU General Public License version 2 only, as |
| 7 | + * published by the Free Software Foundation. |
| 8 | + * |
| 9 | + * This code is distributed in the hope that it will be useful, but WITHOUT |
| 10 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 11 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 12 | + * version 2 for more details (a copy is included in the LICENSE file that |
| 13 | + * accompanied this code). |
| 14 | + * |
| 15 | + * You should have received a copy of the GNU General Public License version |
| 16 | + * 2 along with this work; if not, write to the Free Software Foundation, |
| 17 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 18 | + * |
| 19 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| 20 | + * or visit www.oracle.com if you need additional information or have any |
| 21 | + * questions. |
| 22 | + */ |
| 23 | + |
| 24 | +/* |
| 25 | + * @test |
| 26 | + * @bug 8292088 |
| 27 | + * @requires vm.compiler2.enabled |
| 28 | + * @summary Test that OuterStripMinedLoop and its CountedLoop are both removed after the removal of Opaque1 and 2 nodes |
| 29 | + which allows the loop backedge to be optimized out. |
| 30 | + * @run main/othervm -XX:LoopMaxUnroll=0 -Xcomp |
| 31 | + * -XX:CompileCommand=compileonly,compiler.c2.TestRemoveSingleIterationLoop::test* |
| 32 | + * -XX:CompileCommand=dontinline,compiler.c2.TestRemoveSingleIterationLoop::dontInline |
| 33 | + * compiler.c2.TestRemoveSingleIterationLoop |
| 34 | + * @run main/othervm -XX:LoopMaxUnroll=2 -Xcomp |
| 35 | + * -XX:CompileCommand=compileonly,compiler.c2.TestRemoveSingleIterationLoop::test* |
| 36 | + * -XX:CompileCommand=dontinline,compiler.c2.TestRemoveSingleIterationLoop::dontInline |
| 37 | + * compiler.c2.TestRemoveSingleIterationLoop |
| 38 | + */ |
| 39 | +package compiler.c2; |
| 40 | + |
| 41 | +public class TestRemoveSingleIterationLoop { |
| 42 | + static int N = 400; |
| 43 | + static int x = 3; |
| 44 | + static int y = 3; |
| 45 | + static volatile int[] iArr = new int[N]; |
| 46 | + |
| 47 | + public static void main(String[] args) { |
| 48 | + testKnownLimit(); |
| 49 | + testUnknownLimit(); |
| 50 | + testKnownLimit2(); |
| 51 | + testFuzzer(); |
| 52 | + } |
| 53 | + |
| 54 | + // Upper limit is known (i.e. type of iv phi for i is [-9999..499]). Unroll = 0 to trigger. |
| 55 | + private static void testKnownLimit() { |
| 56 | + int i = -10000; |
| 57 | + int limit = 500; |
| 58 | + |
| 59 | + // These two loops are only required to make sure that we only know that 'i' is 5 after the second loop after CCP. |
| 60 | + int a = 2; |
| 61 | + for (; a < 4; a *= 2); |
| 62 | + for (int b = 2; b < a; b++) { |
| 63 | + i = 5; |
| 64 | + } |
| 65 | + |
| 66 | + if (i < limit + 1) { // Required such that C2 knows that we are always entering the first loop. |
| 67 | + // Loop L1 |
| 68 | + for (; i < limit; i++) { |
| 69 | + // IV_PHI_i = iv phi of i for this loop with type: |
| 70 | + // - Before CPP: [-10000..499] |
| 71 | + // - After CPP: [5..499] |
| 72 | + y = 3; |
| 73 | + } |
| 74 | + |
| 75 | + int j = 6; |
| 76 | + // C2 parses the following loop as: |
| 77 | + // Loop head |
| 78 | + // body (where we do j--) |
| 79 | + // Loop exit check where we already applied j--: j > i - 1 |
| 80 | + while (j > i - 1) { |
| 81 | + // IV_PHI_j = iv phi of j for this loop with type: |
| 82 | + // - Before CPP: [-9998..7] |
| 83 | + j--; |
| 84 | + iArr[23] = 3; |
| 85 | + // At this point i = IV_PHI_i + 1 because i was incremented once more before exiting loop L1. |
| 86 | + // In PhaseIdealLoop::reorg_offsets(), we identify such direct (pre-incremented) usages of an iv phi and |
| 87 | + // add an Opaque2 node to prevent loop-fallout uses. This lowers the register pressure. We replace the |
| 88 | + // direct phi usage in CmpI (annotated with types before CCP): |
| 89 | + // |
| 90 | + // Phi Phi (IV_PHI_i) # [-10000..499] |
| 91 | + // | | |
| 92 | + // | AddI (+1) # [-9999..500] |
| 93 | + // | | |
| 94 | + // | Opaque2 # int |
| 95 | + // | ====> | |
| 96 | + // | AddI (-1) # int |
| 97 | + // | | |
| 98 | + // | CastII # [-10000..499] |
| 99 | + // | | |
| 100 | + // CmpI CmpI # j > i - 1 = IV_PHI_j - 1 > CastII (actually IV_PHI_i) = [-10000..5] > [-10000..499] |
| 101 | + // |
| 102 | + // |
| 103 | + // After CCP, the type of the iv phi IV_PHI_i improves to [5..499] while the type of the CastII does not |
| 104 | + // because the Opaque2 node blocks the type update. When removing the Opaque2 node, we find that the |
| 105 | + // loop only runs for a single time and does not take the backedge: |
| 106 | + // |
| 107 | + // [-10000..5] > [5..499] is false |
| 108 | + // |
| 109 | + // However, we only remove Opaque2 nodes in macro expansion where we also adjust the strip mined loop: |
| 110 | + // We copy the bool node from the CountedLoopEnd to the OuterStripMinedLoopEnd node and adjust the |
| 111 | + // loop exit check of the CountedLoopEnd in such a way that C2 is not able to prove that the loop |
| 112 | + // is only run once. But the OuterStripMinedLoop can now be removed due to the removed Opaque2 nodes |
| 113 | + // and we end up with a CountedLoop node that is strip mined but has no OuterStripMined loop. This |
| 114 | + // results in an assertion failure later when reshaping the graph. |
| 115 | + } |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + // Upper limit is not known (i.e. type of iv phi for i is [-9999..max-1]). Unroll = 0 to trigger. |
| 120 | + private static void testUnknownLimit() { |
| 121 | + int i = -10000; |
| 122 | + int limit = x; |
| 123 | + |
| 124 | + // These two loops are required to make sure that we only know after CCP that 'i' is 1 after the second loop. |
| 125 | + int a = 2; |
| 126 | + for (; a < 4; a *= 2); |
| 127 | + for (int b = 2; b < a; b++) { |
| 128 | + i = 0; |
| 129 | + } |
| 130 | + if (i + 1 < limit) { |
| 131 | + i++; |
| 132 | + for (; i < limit; i++) { |
| 133 | + y = 3; |
| 134 | + } |
| 135 | + int t = 2; |
| 136 | + while (t > i - 1) { |
| 137 | + t--; |
| 138 | + iArr[23] = 3; |
| 139 | + } |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + // Upper limit is known. Unroll = 2 to trigger. |
| 144 | + static int testKnownLimit2() { |
| 145 | + int i = -10000, j; |
| 146 | + int[][] iArr1 = new int[N][N]; |
| 147 | + |
| 148 | + // These two loops are required to make sure that we only know after CCP that 'i' is 1 after the second loop. |
| 149 | + int a = 2; |
| 150 | + for (; a < 4; a *= 2); |
| 151 | + for (int b = 2; b < a; b++) { |
| 152 | + i = 1; |
| 153 | + } |
| 154 | + |
| 155 | + while (++i < 318) { |
| 156 | + dontInline(); |
| 157 | + } |
| 158 | + |
| 159 | + for (j = 6; j > i; j--) { |
| 160 | + // Type of i before CCP: -9999..317 |
| 161 | + // Type of i after CCP: 2..317 |
| 162 | + iArr[1] = 25327; |
| 163 | + } |
| 164 | + |
| 165 | + // This loop is required to trigger the assertion failure. |
| 166 | + for (int y = 0; y < iArr1.length; y++) { |
| 167 | + dontInline(iArr1[2]); |
| 168 | + } |
| 169 | + return i; |
| 170 | + } |
| 171 | + |
| 172 | + // Reduced original fuzzer test. Unroll = 2 to trigger. |
| 173 | + static int testFuzzer() { |
| 174 | + int i = 1, j, iArr1[][] = new int[N][N]; |
| 175 | + while (++i < 318) { |
| 176 | + dontInline(); |
| 177 | + for (j = 5; j > i; j--) { |
| 178 | + iArr[1] = 25327; |
| 179 | + } |
| 180 | + } |
| 181 | + |
| 182 | + // Some more code that is required to trigger the assertion failure. |
| 183 | + for (int y = 0; y < iArr1.length; y++) { |
| 184 | + dontInline(iArr1[2]); |
| 185 | + } |
| 186 | + return i; |
| 187 | + } |
| 188 | + |
| 189 | + static void dontInline() {} |
| 190 | + |
| 191 | + static void dontInline(int[] iArr) {} |
| 192 | +} |
0 commit comments