8281429: PhiNode::Value() is too conservative for tripcount of CountedLoop

Reviewed-by: thartmann, kvn
This commit is contained in:
Roland Westrelin 2022-05-06 08:24:33 +00:00
parent dd06cc638e
commit fa1ca98fff
7 changed files with 403 additions and 19 deletions

@ -1108,18 +1108,55 @@ const Type* PhiNode::Value(PhaseGVN* phase) const {
const TypeInteger* hi = phase->type(limit)->isa_integer(l->bt());
const TypeInteger* stride_t = phase->type(stride)->isa_integer(l->bt());
if (lo != NULL && hi != NULL && stride_t != NULL) { // Dying loops might have TOP here
assert(stride_t->hi_as_long() >= stride_t->lo_as_long(), "bad stride type");
assert(stride_t->is_con(), "bad stride type");
BoolTest::mask bt = l->loopexit()->test_trip();
// If the loop exit condition is "not equal", the condition
// would not trigger if init > limit (if stride > 0) or if
// init < limit if (stride > 0) so we can't deduce bounds
// for the iv from the exit condition.
if (bt != BoolTest::ne) {
if (stride_t->hi_as_long() < 0) { // Down-counter loop
jlong stride_con = stride_t->get_con_as_long(l->bt());
if (stride_con < 0) { // Down-counter loop
swap(lo, hi);
return TypeInteger::make(MIN2(lo->lo_as_long(), hi->lo_as_long()), hi->hi_as_long(), 3, l->bt())->filter_speculative(_type);
} else if (stride_t->lo_as_long() >= 0) {
return TypeInteger::make(lo->lo_as_long(), MAX2(lo->hi_as_long(), hi->hi_as_long()), 3, l->bt())->filter_speculative(_type);
jlong iv_range_lower_limit = lo->lo_as_long();
// Prevent overflow when adding one below
if (iv_range_lower_limit < max_signed_integer(l->bt())) {
// The loop exit condition is: iv + stride > limit (iv is this Phi). So the loop iterates until
// iv + stride <= limit
// We know that: limit >= lo->lo_as_long() and stride <= -1
// So when the loop exits, iv has to be at most lo->lo_as_long() + 1
iv_range_lower_limit += 1; // lo is after decrement
// Exact bounds for the phi can be computed when ABS(stride) greater than 1 if bounds are constant.
if (lo->is_con() && hi->is_con() && hi->lo_as_long() > lo->hi_as_long() && stride_con != -1) {
julong uhi = static_cast<julong>(hi->lo_as_long());
julong ulo = static_cast<julong>(lo->hi_as_long());
julong diff = ((uhi - ulo - 1) / (-stride_con)) * (-stride_con);
julong ufirst = hi->lo_as_long() - diff;
iv_range_lower_limit = reinterpret_cast<jlong &>(ufirst);
assert(iv_range_lower_limit >= lo->lo_as_long() + 1, "should end up with narrower range");
}
}
return TypeInteger::make(MIN2(iv_range_lower_limit, hi->lo_as_long()), hi->hi_as_long(), 3, l->bt())->filter_speculative(_type);
} else if (stride_con >= 0) {
jlong iv_range_upper_limit = hi->hi_as_long();
// Prevent overflow when subtracting one below
if (iv_range_upper_limit > min_signed_integer(l->bt())) {
// The loop exit condition is: iv + stride < limit (iv is this Phi). So the loop iterates until
// iv + stride >= limit
// We know that: limit <= hi->hi_as_long() and stride >= 1
// So when the loop exits, iv has to be at most hi->hi_as_long() - 1
iv_range_upper_limit -= 1;
// Exact bounds for the phi can be computed when ABS(stride) greater than 1 if bounds are constant.
if (lo->is_con() && hi->is_con() && hi->lo_as_long() > lo->hi_as_long() && stride_con != 1) {
julong uhi = static_cast<julong>(hi->lo_as_long());
julong ulo = static_cast<julong>(lo->hi_as_long());
julong diff = ((uhi - ulo - 1) / stride_con) * stride_con;
julong ulast = lo->hi_as_long() + diff;
iv_range_upper_limit = reinterpret_cast<jlong &>(ulast);
assert(iv_range_upper_limit <= hi->hi_as_long() - 1, "should end up with narrower range");
}
}
return TypeInteger::make(lo->lo_as_long(), MAX2(lo->hi_as_long(), iv_range_upper_limit), 3, l->bt())->filter_speculative(_type);
}
}
}

@ -1113,6 +1113,7 @@ bool IdealLoopTree::policy_range_check(PhaseIdealLoop* phase, bool provisional,
BaseCountedLoopNode* cl = _head->as_BaseCountedLoop();
Node *trip_counter = cl->phi();
assert(!cl->is_LongCountedLoop() || bt == T_LONG, "only long range checks in long counted loops");
assert(cl->is_valid_counted_loop(cl->bt()), "only for well formed loops");
// Check loop body for tests of trip-counter plus loop-invariant vs
// loop-invariant.

@ -843,10 +843,20 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) {
}
}
// May not have gone thru igvn yet so don't use _igvn.type(phi) (PhaseIdealLoop::is_counted_loop() sets the iv phi's type)
const TypeInteger* phi_t = phi->bottom_type()->is_integer(bt);
assert(phi_t->hi_as_long() >= phi_t->lo_as_long(), "dead phi?");
iters_limit = checked_cast<int>(MIN2((julong)iters_limit, (julong)(phi_t->hi_as_long() - phi_t->lo_as_long())));
// Take what we know about the number of iterations of the long counted loop into account when computing the limit of
// the inner loop.
const Node* init = head->init_trip();
const TypeInteger* lo = _igvn.type(init)->is_integer(bt);
const TypeInteger* hi = _igvn.type(limit)->is_integer(bt);
if (stride_con < 0) {
swap(lo, hi);
}
if (hi->hi_as_long() <= lo->lo_as_long()) {
// not a loop after all
return false;
}
julong orig_iters = hi->hi_as_long() - lo->lo_as_long();
iters_limit = checked_cast<int>(MIN2((julong)iters_limit, orig_iters));
// We need a safepoint to insert empty predicates for the inner loop.
SafePointNode* safepoint;
@ -4471,6 +4481,7 @@ void PhaseIdealLoop::build_and_optimize() {
// look for RCE candidates and inhibit split_thru_phi
// on just their loop-phi's for this pass of loop opts
if (SplitIfBlocks && do_split_ifs &&
head->as_BaseCountedLoop()->is_valid_counted_loop(head->as_BaseCountedLoop()->bt()) &&
(lpt->policy_range_check(this, true, T_LONG) ||
(head->is_CountedLoop() && lpt->policy_range_check(this, true, T_INT)))) {
lpt->_rce_candidate = 1; // = true

@ -0,0 +1,256 @@
/*
* Copyright (c) 2022, Red Hat, Inc. 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.c2.irTests;
import compiler.lib.ir_framework.*;
/*
* @test
* @bug 8281429
* @summary PhiNode::Value() is too conservative for tripcount of CountedLoop
* @library /test/lib /
* @run driver compiler.c2.irTests.TestCountedLoopPhiValue
*/
public class TestCountedLoopPhiValue {
public static void main(String[] args) {
TestFramework.runWithFlags("-XX:LoopUnrollLimit=0");
}
@Test
@IR(counts = { IRNode.COUNTEDLOOP, "1" })
@IR(failOn = { IRNode.IF })
public static float test1() {
int i = 0;
int j;
float v = 1;
do {
v *= 2;
j = i;
i++;
} while (i < 10);
if (j < 10) {
v *= 2;
}
return v;
}
@Check(test = "test1")
public static void checkTest1(float res) {
if (res != Math.pow(2.0, 11)) {
throw new RuntimeException("incorrect result: " + res);
}
}
@Test
@IR(counts = { IRNode.COUNTEDLOOP, "1" })
@IR(failOn = { IRNode.IF })
public static float test2() {
int i = 0;
int j;
float v = 1;
do {
v *= 2;
j = i;
i += 2;
} while (i < 10);
if (j < 9) {
v *= 2;
}
return v;
}
@Check(test = "test2")
public static void checkTest2(float res) {
if (res != Math.pow(2.0, 6)) {
throw new RuntimeException("incorrect result: " + res);
}
}
@Test
@IR(counts = { IRNode.COUNTEDLOOP, "1" })
@IR(failOn = { IRNode.IF })
public static float test3() {
int i = 10;
int j;
float v = 1;
do {
v *= 2;
j = i;
i--;
} while (i > 0);
if (j > 0) {
v *= 2;
}
return v;
}
@Check(test = "test3")
public static void checkTest3(float res) {
if (res != Math.pow(2.0, 11)) {
throw new RuntimeException("incorrect result: " + res);
}
}
@Test
@IR(counts = { IRNode.COUNTEDLOOP, "1" })
@IR(failOn = { IRNode.IF })
public static float test4() {
int i = 10;
int j;
float v = 1;
do {
v *= 2;
j = i;
i -= 2;
} while (i > 0);
if (j > 1) {
v *= 2;
}
return v;
}
@Check(test = "test4")
public static void checkTest4(float res) {
if (res != Math.pow(2.0, 6)) {
throw new RuntimeException("incorrect result: " + res);
}
}
final static int int_stride = Integer.MAX_VALUE / 5;
final static int test5_limit = Integer.MAX_VALUE - int_stride;
final static int test5_iterations = Integer.divideUnsigned(test5_limit - Integer.MIN_VALUE + (int_stride - 1), int_stride);
final static int last_test5_iteration = Integer.MIN_VALUE + Integer.divideUnsigned(test5_limit - Integer.MIN_VALUE, int_stride) * int_stride;
@Test
@IR(counts = { IRNode.COUNTEDLOOP, "1" })
@IR(failOn = { IRNode.IF })
public static float test5() {
int i = Integer.MIN_VALUE;
int j;
float v = 1;
do {
v *= 2;
j = i;
i += int_stride;
} while (i < test5_limit);
if (j < last_test5_iteration + 1) {
v *= 2;
}
return v;
}
@Check(test = "test5")
public static void checkTest5(float res) {
if (res != Math.pow(2.0, test5_iterations+1)) {
throw new RuntimeException("incorrect result: " + res);
}
}
final static int test6_limit = Integer.MIN_VALUE + int_stride;
final static int test6_iterations = Integer.divideUnsigned(Integer.MAX_VALUE - test6_limit + (int_stride - 1), int_stride);
final static int last_test6_iteration = Integer.MAX_VALUE - Integer.divideUnsigned(Integer.MAX_VALUE - test6_limit, int_stride) * int_stride;
@Test
@IR(counts = { IRNode.COUNTEDLOOP, "1" })
@IR(failOn = { IRNode.IF })
public static float test6() {
int i = Integer.MAX_VALUE;
int j;
float v = 1;
do {
v *= 2;
j = i;
i -= int_stride;
} while (i > test6_limit);
if (j > last_test6_iteration - 1) {
v *= 2;
}
return v;
}
@Check(test = "test6")
public static void checkTest6(float res) {
if (res != Math.pow(2.0, test6_iterations+1)) {
throw new RuntimeException("incorrect result: " + res);
}
}
final static long long_stride = Long.MAX_VALUE / 5;
final static long test7_limit = Long.MAX_VALUE - long_stride;
final static long test7_iterations = Long.divideUnsigned(test7_limit - Long.MIN_VALUE + (long_stride - 1), long_stride);
final static long last_test7_iteration = Long.MIN_VALUE + Long.divideUnsigned(test7_limit - Long.MIN_VALUE, long_stride) * long_stride;
@Test
@IR(counts = { IRNode.LONGCOUNTEDLOOP, "1", IRNode.IF, "1" })
public static float test7() {
long i = Long.MIN_VALUE;
long j;
float v = 1;
do {
v *= 2;
j = i;
i += long_stride;
} while (i < test7_limit);
if (j < last_test7_iteration + 1) {
v *= 2;
}
return v;
}
@Check(test = "test7")
public static void checkTest7(float res) {
if (res != Math.pow(2.0, test7_iterations+1)) {
throw new RuntimeException("incorrect result: " + res);
}
}
final static long test8_limit = Long.MIN_VALUE + long_stride;
final static long test8_iterations = Long.divideUnsigned(Long.MAX_VALUE - test8_limit + (long_stride - 1), long_stride);
final static long last_test8_iteration = Long.MAX_VALUE - Long.divideUnsigned(Long.MAX_VALUE - test8_limit, long_stride) * long_stride;
@Test
@IR(counts = { IRNode.LONGCOUNTEDLOOP, "1", IRNode.IF, "1" })
public static float test8() {
long i = Long.MAX_VALUE;
long j;
float v = 1;
do {
v *= 2;
j = i;
i -= long_stride;
} while (i > test8_limit);
if (j > last_test8_iteration - 1) {
v *= 2;
}
return v;
}
@Check(test = "test8")
public static void checkTest8(float res) {
if (res != Math.pow(2.0, test8_iterations+1)) {
throw new RuntimeException("incorrect result: " + res);
}
}
}

@ -29,8 +29,8 @@ import java.util.Random;
/*
* @test
* @bug 8278228
* @summary C2: Improve identical back-to-back if elimination
* @bug 8282592
* @summary C2: assert(false) failed: graph should be schedulable
* @library /test/lib /
* @requires vm.compiler2.enabled
* @run driver compiler.c2.irTests.TestSkeletonPredicates
@ -45,7 +45,7 @@ public class TestSkeletonPredicates {
static volatile int barrier;
@ForceInline
static boolean test1_helper(int start, int stop, double[] array1, double[] array2) {
static boolean test1_helper(int start, int stop, double[] array1) {
for (int i = start; i < stop; i++) {
if ((i % 2) == 0) {
array1[i] = 42.42;
@ -57,12 +57,11 @@ public class TestSkeletonPredicates {
}
@Test
@IR(counts = { IRNode.COUNTEDLOOP, "3" })
static double[] test1(int stop, double[] array2) {
double[] array1 = null;
array1 = new double[10];
@IR(counts = { IRNode.COUNTEDLOOP, "2" })
static double[] test1(int stop) {
double[] array1 = new double[10];
for (int j = 0; j < stop; j++) {
if (test1_helper(8, j, array1, array2)) {
if (test1_helper(8, j, array1)) {
return null;
}
}
@ -73,7 +72,7 @@ public class TestSkeletonPredicates {
void test1_runner() {
double[] array2 = new double[10];
double[] array3 = new double[1000];
test1_helper(1, 1000, array3, array3);
test1(11, array3);
test1_helper(1, 1000, array3);
test1(11);
}
}

@ -116,6 +116,7 @@ public class IRNode {
public static final String COUNTEDLOOP = START + "CountedLoop\\b" + MID + END;
public static final String COUNTEDLOOP_MAIN = START + "CountedLoop\\b" + MID + "main" + END;
public static final String OUTERSTRIPMINEDLOOP = START + "OuterStripMinedLoop\\b" + MID + END;
public static final String LONGCOUNTEDLOOP = START + "LongCountedLoop\\b" + MID + END;
public static final String IF = START + "If\\b" + MID + END;
public static final String CALL = START + "Call.*Java" + MID + END;
@ -170,6 +171,7 @@ public class IRNode {
public static final String MUL = START + "Mul(I|L|F|D)" + MID + END;
public static final String MUL_I = START + "MulI" + MID + END;
public static final String MUL_L = START + "MulL" + MID + END;
public static final String MUL_F = START + "MulF" + MID + END;
public static final String DIV = START + "(NoOvf)?Div(I|L|F|D)" + MID + END;
public static final String DIV_L = START + "(NoOvf)?DivL" + MID + END;
public static final String CON_I = START + "ConI" + MID + END;

@ -0,0 +1,78 @@
/*
* Copyright (c) 2022, Red Hat, Inc. 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 8281429
* @summary PhiNode::Value() is too conservative for tripcount of CountedLoop
* @run main/othervm -XX:-TieredCompilation -Xbatch TestCountedLoopPhiValue
*/
public class TestCountedLoopPhiValue {
public static void main(String[] args) {
test1();
test2();
}
private static void test1() {
for (long l = (Long.MAX_VALUE - 1); l != (Long.MIN_VALUE + 100_000); l++) {
if (l == 0) {
throw new RuntimeException("Test failed");
}
}
}
private static void test2() {
for (int i = 1; i < 10 * 182 * 138; i++) {
iMeth(-9, -9);
}
}
public static final int N = 400;
public static long instanceCount=-2L;
public static long lArrFld[]=new long[N];
public static void iMeth(int i6, int i7) {
double d1;
int i8, i10, i11=30785, i12=8;
long l2;
byte by=58;
d1 = i7;
i8 = 1;
while (++i8 < 342) {
for (l2 = 1; l2 < 5; l2++) {
i6 -= (int)d1;
for (i10 = 1; i10 < 2; i10++) {
i7 += (int)l2;
i12 *= i11;
i11 -= i12;
instanceCount += i10;
lArrFld[i8 - 1] = by;
}
}
}
}
}