jdk-24/test/hotspot/jtreg/compiler/predicates/TestHoistedPredicateForNonRangeCheck.java

183 lines
7.3 KiB
Java

/*
* Copyright (c) 2023, 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 8307683
* @library /test/lib /
* @requires vm.compiler2.enabled
* @summary Tests that IfNode is not wrongly chosen as range check by Loop Predication leading to crashes and wrong executions.
* @run main/othervm -Xcomp -XX:CompileCommand=compileonly,compiler.predicates.TestHoistedPredicateForNonRangeCheck::test*
* compiler.predicates.TestHoistedPredicateForNonRangeCheck
* @run main/othervm -Xcomp -XX:CompileCommand=compileonly,compiler.predicates.TestHoistedPredicateForNonRangeCheck::test*
* -XX:LoopMaxUnroll=0 compiler.predicates.TestHoistedPredicateForNonRangeCheck
*/
/*
* @test
* @bug 8307683
* @library /test/lib /
* @summary Tests that IfNode is not wrongly chosen as range check by Loop Predication leading to crashes and wrong executions.
* @run main/othervm -Xbatch compiler.predicates.TestHoistedPredicateForNonRangeCheck calendar
*/
package compiler.predicates;
import jdk.test.lib.Asserts;
import java.util.Calendar;
import java.util.Date;
public class TestHoistedPredicateForNonRangeCheck {
static int iFld, iFld2;
static int[] iArr = new int[100];
public static void main(String[] args) {
if (args.length == 0) {
Integer.compareUnsigned(34, 34); // Ensure Integer class is loaded and we do not emit a trap inside test() for it.
for (int i = 0; i < 2; i++) {
iFld = 0;
iFld2 = 0;
test();
Asserts.assertEQ(iFld, 3604, "wrong value");
Asserts.assertEQ(iFld2, 400, "wrong value");
}
for (int i = 0; i < 2000; i++) {
iFld = -100;
testRangeCheckNode();
}
iFld = -1;
iFld2 = 0;
testRangeCheckNode();
Asserts.assertEQ(iFld2, 36, "wrong value");
} else {
boolean flag = false;
for (int i = 0; i < 10000; i++) {
testCalendar1();
testCalendar2(flag);
}
}
}
public static void test() {
for (int i = -1; i < 1000; i++) {
// We hoist this check and insert a Hoisted Predicate for the lower and upper bound:
// -1 >=u 100 && 1000 >= u 100 -> always true and the predicates are removed.
// Template Assertion Predicates, however, are kept. When splitting this loop further, we insert an Assertion
// Predicate which fails for i = 0 and we halt.
// When not splitting this loop (with LoopMaxUnroll=0), we have a wrong execution due to never executing
// iFld2++ (we remove the check and the branch with the trap when creating the Hoisted Predicates).
if (Integer.compareUnsigned(i, 100) < 0) {
iFld2++;
Float.isNaN(34); // Float class is unloaded with -Xcomp -> inserts trap
} else {
iFld++;
}
// Same but flipped condition and moved trap to other branch - result is the same.
if (Integer.compareUnsigned(i, 100) >= 0) { // Loop Predication creates a Hoisted Range Check Predicate due to trap with Float.isNan().
iFld++;
} else {
iFld2++;
Float.isNaN(34); // Float class is unloaded with -Xcomp -> inserts trap
}
// Same but with LoadRangeNode.
if (Integer.compareUnsigned(i, iArr.length) >= 0) { // Loop Predication creates a Hoisted Range Check Predicate due to trap with Float.isNan().
iFld++;
} else {
iFld2++;
Float.isNaN(34); // Float class is unloaded with -Xcomp -> inserts trap
}
// Same but with LoadRangeNode and flipped condition and moved trap to other branch - result is the same.
if (Integer.compareUnsigned(i, iArr.length) >= 0) { // Loop Predication creates a Hoisted Range Check Predicate due to trap with Float.isNan().
iFld++;
} else {
iFld2++;
Float.isNaN(34); // Float class is unloaded with -Xcomp -> inserts trap
}
}
}
static void testRangeCheckNode() {
int array[] = new int[34];
// Hoisted Range Check Predicate with flipped bool because trap is on success proj and no trap on false proj due
// to catching exception:
// iFld >=u 34 && iFld+36 >=u 34
// This is always false for first 2000 iterations where, initially, iFld = -100
// It is still true in the last iteration where, initially, iFld = -1. But suddenly, in the second iteration,
// where iFld = 0, we would take the true projection for the first time - but we removed that branch when
// creating the Hoisted Range Check Predicate. We therefore run into the same problem as with test(): We either
// halt due to Assertion Predicates catching this case or we have a wrong execution (iFld2 never updated).
for (int i = 0; i < 37; i++) {
try {
array[iFld] = 34; // Normal RangeCheckNode
iFld2++;
Math.ceil(34); // Never taken and unloaded -> trap
} catch (Exception e) {
// False Proj of RangeCheckNode
iFld++;
}
}
}
// Reported in JDK-8307683
static void testCalendar1() {
Calendar c = Calendar.getInstance();
c.setLenient(false);
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.getTime();
}
// Reported in JDK-8307978
static void testCalendar2(boolean flag) {
flag = !flag;
Calendar timespan = removeTime(new Date(), flag);
timespan.getTime();
}
static Calendar removeTime(Date date, boolean flag) {
Calendar calendar = Calendar.getInstance();
if (flag) {
calendar.setLenient(false);
}
calendar.setTime(date);
calendar = removeTime(calendar);
return calendar;
}
static Calendar removeTime(Calendar calendar) {
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar;
}
}