1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
/*
* Copyright (c) 2025, 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.
*/
package compiler.loopopts.superword;
import compiler.lib.ir_framework.*;
/*
* @test
* @bug 8350756
* @summary Test case where the multiversion fast_loop disappears, and we should
* constant fold the multiversion_if, to remove the slow_loop.
* @library /test/lib /
* @run driver compiler.loopopts.superword.TestMultiversionRemoveUselessSlowLoop
*/
public class TestMultiversionRemoveUselessSlowLoop {
public static void main(String[] args) {
TestFramework framework = new TestFramework(TestMultiversionRemoveUselessSlowLoop.class);
// No traps means we cannot use the predicates version for SuperWord / AutoVectorization,
// and instead use multiversioning directly.
framework.addFlags("-XX:-TieredCompilation", "-XX:PerMethodTrapLimit=0");
framework.setDefaultWarmup(0); // simulates Xcomp
framework.start();
}
public static final int SIZE = 20;
public static final int[] a = new int[SIZE];
public static final int[] b = new int[SIZE];
public static final int SIZE2 = 10_000;
public static final int[] a2 = new int[SIZE2];
public static final int[] b2 = new int[SIZE2];
@Test
@IR(counts = {"pre .* multiversion_fast", "= 2", // regular pre-main-post for both loops
"main .* multiversion_fast", "= 2",
"post .* multiversion_fast", "= 2",
"multiversion_delayed_slow", "= 2", // both have the delayed slow_loop
"multiversion", "= 8", // nothing unexpected
IRNode.OPAQUE_MULTIVERSIONING, "= 2"}, // Both multiversion_if are still here
applyIfPlatform = {"64-bit", "true"},
applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"},
phase = CompilePhase.PHASEIDEALLOOP1)
@IR(counts = {"pre .* multiversion_fast", "= 2",
"main .* multiversion_fast", "= 1", // The first main loop is fully unrolled
"post .* multiversion_fast", "= 3", // the second loop is vectorized, and has a vectorized post loop
"multiversion_delayed_slow", "= 1", // As a consequence of the first main loop being removed, we constant fold the multiversion_if
"multiversion", "= 7", // nothing unexpected
IRNode.OPAQUE_MULTIVERSIONING, "= 1"}, // The multiversion_if of the first loop was constant folded, because the main loop disappeared.
applyIfPlatform = {"64-bit", "true"},
applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"},
phase = CompilePhase.PHASEIDEALLOOP_ITERATIONS)
@IR(counts = {"pre .* multiversion_fast.*", ">= 1", // In some cases, the pre loop of the first loop also disappears because it only has a single iteration
"pre .* multiversion_fast.*", "<= 2", // but not in other cases the pre loop of the first loop remains.
"main .* multiversion_fast", "= 1",
"post .* multiversion_fast", "= 3",
"multiversion_delayed_slow", "= 0", // The second loop's multiversion_if was also not used, so it is constant folded after loop opts.
"multiversion", ">= 5", // nothing unexpected
"multiversion", "<= 6", // nothing unexpected
IRNode.OPAQUE_MULTIVERSIONING, "= 0"}, // After loop-opts, we also constant fold the multiversion_if of the second loop, as it is unused.
applyIfPlatform = {"64-bit", "true"},
applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"},
phase = CompilePhase.PRINT_IDEAL)
public static void testIR() {
// This loop is short, and the multiversion_fast main loop eventuall is fully unrolled.
for (int i = 0; i < SIZE; i++) {
a[i] = b[i];
}
// We take this second loop with a larger limit so that loop opts keeps going once the loop
// above is fully optimized. It also gives us a reference where the main loop of the
// multiverion fast_loop does not disappear.
for (int i = 0; i < SIZE2; i++) {
a2[i] = b2[i];
}
}
static long instanceCount;
static int iFld;
static int iFld1;
// The inner loop is Multiversioned, then PreMainPost and Unroll.
// Eventually, both the fast and slow loops (pre main and post) disappear,
// and leave us with a simple if-diamond using the multiversion_if.
//
// Verification code in PhaseIdealLoop::conditional_move finds this diamond
// and expects a Bool but gets an OpaqueMultiversioning instead.
//
// If we let the multiversion_if constant fold soon after the main fast loop
// disappears, then this issue does not occur any more.
@Test
public static void testCrash() {
boolean b2 = true;
for (int i = 0; i < 1000; i++) {
for (int i21 = 82; i21 > 9; --i21) {
if (b2)
break;
iFld1 = iFld;
b2 = true;
}
instanceCount = iFld1;
}
}
class Unloaded {
static void unloaded() {}
}
static int f;
// The outer loop is eventually Multiversioned, then PreMainPost and Unroll.
// Then the loops disappear during IGVN, and in the next loop-opts phase, the
// OpaqueMultiversioning is marked useless, but then we already run
// PhaseIdealLoop::conditional_move before the next IGVN round, and find a
// useless OpaqueMultiversioning instead of a BoolNode.
@Test
@Arguments(values = { Argument.NUMBER_42 })
static void testCrash2(int y) {
int x = 53446;
for (int i = 12; i < 376; i++) {
if (x != 0) {
// Uncommon trap because the class is not yet loaded.
Unloaded.unloaded();
}
for (int k = 1; k < 4; k++) {
y += 1;
}
x += f;
}
}
}
|