mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8356813: Improve Mod(I|L)Node::Value
Reviewed-by: epeter, qamai
This commit is contained in:
parent
ca89cd06d3
commit
c7f014ed49
@ -1198,44 +1198,76 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
}
|
||||
|
||||
//------------------------------Value------------------------------------------
|
||||
const Type* ModINode::Value(PhaseGVN* phase) const {
|
||||
static const Type* mod_value(const PhaseGVN* phase, const Node* in1, const Node* in2, const BasicType bt) {
|
||||
assert(bt == T_INT || bt == T_LONG, "unexpected basic type");
|
||||
// Either input is TOP ==> the result is TOP
|
||||
const Type *t1 = phase->type( in(1) );
|
||||
const Type *t2 = phase->type( in(2) );
|
||||
if( t1 == Type::TOP ) return Type::TOP;
|
||||
if( t2 == Type::TOP ) return Type::TOP;
|
||||
const Type* t1 = phase->type(in1);
|
||||
const Type* t2 = phase->type(in2);
|
||||
if (t1 == Type::TOP) { return Type::TOP; }
|
||||
if (t2 == Type::TOP) { return Type::TOP; }
|
||||
|
||||
// We always generate the dynamic check for 0.
|
||||
// 0 MOD X is 0
|
||||
if( t1 == TypeInt::ZERO ) return TypeInt::ZERO;
|
||||
if (t1 == TypeInteger::zero(bt)) { return t1; }
|
||||
|
||||
// X MOD X is 0
|
||||
if (in(1) == in(2)) {
|
||||
return TypeInt::ZERO;
|
||||
if (in1 == in2) {
|
||||
return TypeInteger::zero(bt);
|
||||
}
|
||||
|
||||
// Either input is BOTTOM ==> the result is the local BOTTOM
|
||||
const Type *bot = bottom_type();
|
||||
if( (t1 == bot) || (t2 == bot) ||
|
||||
(t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) )
|
||||
return bot;
|
||||
|
||||
const TypeInt *i1 = t1->is_int();
|
||||
const TypeInt *i2 = t2->is_int();
|
||||
if( !i1->is_con() || !i2->is_con() ) {
|
||||
if( i1->_lo >= 0 && i2->_lo >= 0 )
|
||||
return TypeInt::POS;
|
||||
// If both numbers are not constants, we know little.
|
||||
return TypeInt::INT;
|
||||
}
|
||||
// Mod by zero? Throw exception at runtime!
|
||||
if( !i2->get_con() ) return TypeInt::POS;
|
||||
if (t2 == TypeInteger::zero(bt)) {
|
||||
return Type::TOP;
|
||||
}
|
||||
|
||||
// We must be modulo'ing 2 float constants.
|
||||
// Check for min_jint % '-1', result is defined to be '0'.
|
||||
if( i1->get_con() == min_jint && i2->get_con() == -1 )
|
||||
return TypeInt::ZERO;
|
||||
const TypeInteger* i1 = t1->is_integer(bt);
|
||||
const TypeInteger* i2 = t2->is_integer(bt);
|
||||
if (i1->is_con() && i2->is_con()) {
|
||||
// We must be modulo'ing 2 int constants.
|
||||
// Special case: min_jlong % '-1' is UB, and e.g., x86 triggers a division error.
|
||||
// Any value % -1 is 0, so we can return 0 and avoid that scenario.
|
||||
if (i2->get_con_as_long(bt) == -1) {
|
||||
return TypeInteger::zero(bt);
|
||||
}
|
||||
return TypeInteger::make(i1->get_con_as_long(bt) % i2->get_con_as_long(bt), bt);
|
||||
}
|
||||
// We checked that t2 is not the zero constant. Hence, at least i2->_lo or i2->_hi must be non-zero,
|
||||
// and hence its absoute value is bigger than zero. Hence, the magnitude of the divisor (i.e. the
|
||||
// largest absolute value for any value in i2) must be in the range [1, 2^31] or [1, 2^63], depending
|
||||
// on the BasicType.
|
||||
julong divisor_magnitude = MAX2(g_uabs(i2->lo_as_long()), g_uabs(i2->hi_as_long()));
|
||||
// JVMS lrem bytecode: "the magnitude of the result is always less than the magnitude of the divisor"
|
||||
// "less than" means we can subtract 1 to get an inclusive upper bound in [0, 2^31-1] or [0, 2^63-1], respectively
|
||||
jlong hi = static_cast<jlong>(divisor_magnitude - 1);
|
||||
jlong lo = -hi;
|
||||
// JVMS lrem bytecode: "the result of the remainder operation can be negative only if the dividend
|
||||
// is negative and can be positive only if the dividend is positive"
|
||||
// Note that with a dividend with bounds e.g. lo == -4 and hi == -1 can still result in values
|
||||
// below lo; i.e., -3 % 3 == 0.
|
||||
// That means we cannot restrict the bound that is closer to zero beyond knowing its sign (or zero).
|
||||
if (i1->hi_as_long() <= 0) {
|
||||
// all dividends are not positive, so the result is not positive
|
||||
hi = 0;
|
||||
// if the dividend is known to be closer to zero, use that as a lower limit
|
||||
lo = MAX2(lo, i1->lo_as_long());
|
||||
} else if (i1->lo_as_long() >= 0) {
|
||||
// all dividends are not negative, so the result is not negative
|
||||
lo = 0;
|
||||
// if the dividend is known to be closer to zero, use that as an upper limit
|
||||
hi = MIN2(hi, i1->hi_as_long());
|
||||
} else {
|
||||
// Mixed signs, so we don't know the sign of the result, but the result is
|
||||
// either the dividend itself or a value closer to zero than the dividend,
|
||||
// and it is closer to zero than the divisor.
|
||||
// As we know i1->_lo < 0 and i1->_hi > 0, we can use these bounds directly.
|
||||
lo = MAX2(lo, i1->lo_as_long());
|
||||
hi = MIN2(hi, i1->hi_as_long());
|
||||
}
|
||||
return TypeInteger::make(lo, hi, MAX2(i1->_widen, i2->_widen), bt);
|
||||
}
|
||||
|
||||
return TypeInt::make( i1->get_con() % i2->get_con() );
|
||||
const Type* ModINode::Value(PhaseGVN* phase) const {
|
||||
return mod_value(phase, in(1), in(2), T_INT);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
@ -1464,43 +1496,7 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
|
||||
//------------------------------Value------------------------------------------
|
||||
const Type* ModLNode::Value(PhaseGVN* phase) const {
|
||||
// Either input is TOP ==> the result is TOP
|
||||
const Type *t1 = phase->type( in(1) );
|
||||
const Type *t2 = phase->type( in(2) );
|
||||
if( t1 == Type::TOP ) return Type::TOP;
|
||||
if( t2 == Type::TOP ) return Type::TOP;
|
||||
|
||||
// We always generate the dynamic check for 0.
|
||||
// 0 MOD X is 0
|
||||
if( t1 == TypeLong::ZERO ) return TypeLong::ZERO;
|
||||
// X MOD X is 0
|
||||
if (in(1) == in(2)) {
|
||||
return TypeLong::ZERO;
|
||||
}
|
||||
|
||||
// Either input is BOTTOM ==> the result is the local BOTTOM
|
||||
const Type *bot = bottom_type();
|
||||
if( (t1 == bot) || (t2 == bot) ||
|
||||
(t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) )
|
||||
return bot;
|
||||
|
||||
const TypeLong *i1 = t1->is_long();
|
||||
const TypeLong *i2 = t2->is_long();
|
||||
if( !i1->is_con() || !i2->is_con() ) {
|
||||
if( i1->_lo >= CONST64(0) && i2->_lo >= CONST64(0) )
|
||||
return TypeLong::POS;
|
||||
// If both numbers are not constants, we know little.
|
||||
return TypeLong::LONG;
|
||||
}
|
||||
// Mod by zero? Throw exception at runtime!
|
||||
if( !i2->get_con() ) return TypeLong::POS;
|
||||
|
||||
// We must be modulo'ing 2 float constants.
|
||||
// Check for min_jint % '-1', result is defined to be '0'.
|
||||
if( i1->get_con() == min_jlong && i2->get_con() == -1 )
|
||||
return TypeLong::ZERO;
|
||||
|
||||
return TypeLong::make( i1->get_con() % i2->get_con() );
|
||||
return mod_value(phase, in(1), in(2), T_LONG);
|
||||
}
|
||||
|
||||
Node *UModLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
|
||||
292
test/hotspot/jtreg/compiler/c2/gvn/ModINodeValueTests.java
Normal file
292
test/hotspot/jtreg/compiler/c2/gvn/ModINodeValueTests.java
Normal file
@ -0,0 +1,292 @@
|
||||
/*
|
||||
* 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.c2.gvn;
|
||||
|
||||
import compiler.lib.generators.Generator;
|
||||
import compiler.lib.generators.Generators;
|
||||
import compiler.lib.generators.RestrictableGenerator;
|
||||
import compiler.lib.ir_framework.DontCompile;
|
||||
import compiler.lib.ir_framework.ForceInline;
|
||||
import compiler.lib.ir_framework.IR;
|
||||
import compiler.lib.ir_framework.IRNode;
|
||||
import compiler.lib.ir_framework.Run;
|
||||
import compiler.lib.ir_framework.Test;
|
||||
import compiler.lib.ir_framework.TestFramework;
|
||||
import jdk.test.lib.Asserts;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8356813
|
||||
* @summary Test that Value method of ModINode is working as expected.
|
||||
* @key randomness
|
||||
* @library /test/lib /
|
||||
* @run driver compiler.c2.gvn.ModINodeValueTests
|
||||
*/
|
||||
public class ModINodeValueTests {
|
||||
private static final RestrictableGenerator<Integer> INT_GEN = Generators.G.ints();
|
||||
private static final int POS_INT = INT_GEN.restricted(1, Integer.MAX_VALUE).next();
|
||||
private static final int NEG_INT = INT_GEN.restricted(Integer.MIN_VALUE, -1).next();
|
||||
|
||||
public static void main(String[] args) {
|
||||
TestFramework.run();
|
||||
}
|
||||
|
||||
@Run(test = {
|
||||
"nonNegativeDividend", "nonNegativeDividendInRange",
|
||||
"negativeDividend", "negativeDividendInRange",
|
||||
"modByKnownBoundsUpper", "modByKnownBoundsUpperInRange",
|
||||
"modByKnownBoundsLower", "modByKnownBoundsLowerInRange",
|
||||
"modByKnownBoundsLimitedByDividendUpper", "modByKnownBoundsLimitedByDividendUpperInRange",
|
||||
"modByKnownBoundsLimitedByDividendLower", "modByKnownBoundsLimitedByDividendLowerInRange",
|
||||
"testRandomLimits"
|
||||
})
|
||||
public void runMethod() {
|
||||
int a = INT_GEN.next();
|
||||
int b = INT_GEN.next();
|
||||
|
||||
int min = Integer.MIN_VALUE;
|
||||
int max = Integer.MAX_VALUE;
|
||||
|
||||
assertResult(0, 0);
|
||||
assertResult(a, b);
|
||||
assertResult(min, min);
|
||||
assertResult(max, max);
|
||||
}
|
||||
|
||||
@DontCompile
|
||||
public void assertResult(int x, int y) {
|
||||
Asserts.assertEQ(x != 0 && POS_INT % x < 0, nonNegativeDividend(x));
|
||||
Asserts.assertEQ(x != 0 && POS_INT % x <= 0, nonNegativeDividendInRange(x));
|
||||
Asserts.assertEQ(x != 0 && NEG_INT % x > 0, negativeDividend(x));
|
||||
Asserts.assertEQ(x != 0 && NEG_INT % x >= 0, negativeDividendInRange(x));
|
||||
Asserts.assertEQ(x % (((byte) y) + 129) > 255, modByKnownBoundsUpper(x, y));
|
||||
Asserts.assertEQ(x % (((byte) y) + 129) >= 255, modByKnownBoundsUpperInRange(x, y));
|
||||
Asserts.assertEQ(x % (((byte) y) + 129) < -255, modByKnownBoundsLower(x, y));
|
||||
Asserts.assertEQ(x % (((byte) y) + 129) <= -255, modByKnownBoundsLowerInRange(x, y));
|
||||
Asserts.assertEQ(((byte) x) % (((char) y) + 1) > 127, modByKnownBoundsLimitedByDividendUpper(x, y));
|
||||
Asserts.assertEQ(((byte) x) % (((char) y) + 1) >= 127, modByKnownBoundsLimitedByDividendUpperInRange(x, y));
|
||||
Asserts.assertEQ(((byte) x) % (((char) y) + 1) < -128, modByKnownBoundsLimitedByDividendLower(x, y));
|
||||
Asserts.assertEQ(((byte) x) % (((char) y) + 1) <= -128, modByKnownBoundsLimitedByDividendLowerInRange(x, y));
|
||||
|
||||
int res;
|
||||
try {
|
||||
res = testRandomLimitsInterpreted(x, y);
|
||||
} catch (ArithmeticException _) {
|
||||
try {
|
||||
testRandomLimits(x, y);
|
||||
Asserts.fail("Expected ArithmeticException");
|
||||
return; // unreachable
|
||||
} catch (ArithmeticException _) {
|
||||
return; // test succeeded, no result to assert
|
||||
}
|
||||
}
|
||||
Asserts.assertEQ(res, testRandomLimits(x, y));
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MOD_I, IRNode.CMP_I})
|
||||
// The sign of the result of % is the same as the sign of the dividend,
|
||||
// i.e., POS_INT % x < 0 => false.
|
||||
public boolean nonNegativeDividend(int x) {
|
||||
return x != 0 && POS_INT % x < 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_I, "1"})
|
||||
// The sign of the result of % is the same as the sign of the dividend,
|
||||
// i.e., POS_INT % x < 0 => false.
|
||||
// This uses <= to verify the % is not optimized away
|
||||
public boolean nonNegativeDividendInRange(int x) {
|
||||
return x != 0 && POS_INT % x <= 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MOD_I, IRNode.CMP_I})
|
||||
// The sign of the result of % is the same as the sign of the dividend,
|
||||
// i.e., NEG_INT % x > 0 => false.
|
||||
public boolean negativeDividend(int x) {
|
||||
return x != 0 && NEG_INT % x > 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_I, "1"})
|
||||
// The sign of the result of % is the same as the sign of the dividend,
|
||||
// i.e., NEG_INT % x > 0 => false.
|
||||
// This uses >= to verify the % is not optimized away
|
||||
public boolean negativeDividendInRange(int x) {
|
||||
return x != 0 && NEG_INT % x >= 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MOD_I, IRNode.CMP_I})
|
||||
// The magnitude of the result is less than the divisor.
|
||||
public boolean modByKnownBoundsUpper(int x, int y) {
|
||||
// d = ((byte) y) + 129 => [1, 256] divisor
|
||||
// x % d => [-255, 255]
|
||||
return x % (((byte) y) + 129) > 255;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_I, "1"})
|
||||
// The magnitude of the result is less than the divisor.
|
||||
public boolean modByKnownBoundsUpperInRange(int x, int y) {
|
||||
// d = ((byte) y) + 129 => [1, 256] divisor
|
||||
// x % d => [-255, 255]
|
||||
// in bounds, cannot optimize
|
||||
return x % (((byte) y) + 129) >= 255;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MOD_I, IRNode.CMP_I})
|
||||
// The magnitude of the result is less than the divisor
|
||||
public boolean modByKnownBoundsLower(int x, int y) {
|
||||
// d = ((byte) y) + 129 => [1, 256] divisor
|
||||
// x % d => [-255, 255]
|
||||
return x % (((byte) y) + 129) < -255;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_I, "1"})
|
||||
// The magnitude of the result is less than the divisor
|
||||
public boolean modByKnownBoundsLowerInRange(int x, int y) {
|
||||
// d = ((byte) y) + 129 => [1, 256] divisor
|
||||
// x % d => [-255, 255]
|
||||
// in bounds, cannot optimize
|
||||
return x % (((byte) y) + 129) <= -255;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MOD_I, IRNode.CMP_I})
|
||||
// The result is closer to zero than or equal to the dividend.
|
||||
public boolean modByKnownBoundsLimitedByDividendUpper(int x, int y) {
|
||||
// d = ((char) y) + 1 => [1, 65536] divisor
|
||||
// e = ((byte) x) => [-128, 127]
|
||||
// e % d => [-128, 127]
|
||||
return ((byte) x) % (((char) y) + 1) > 127;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_I, "1"})
|
||||
// The result is closer to zero than or equal to the dividend.
|
||||
public boolean modByKnownBoundsLimitedByDividendUpperInRange(int x, int y) {
|
||||
// d = ((char) y) + 1 => [1, 65536] divisor
|
||||
// e = ((byte) x) => [-128, 127]
|
||||
// e % d => [-128, 127]
|
||||
// in bounds, cannot optimize
|
||||
return ((byte) x) % (((char) y) + 1) >= 127;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MOD_I, IRNode.CMP_I})
|
||||
// The result is closer to zero than or equal to the dividend.
|
||||
public boolean modByKnownBoundsLimitedByDividendLower(int x, int y) {
|
||||
// d = ((char) y) + 1 => [1, 65536] divisor
|
||||
// e = ((byte) x) => [-128, 127]
|
||||
// e % d => [-128, 127]
|
||||
return ((byte) x) % (((char) y) + 1) < -128;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_I, "1"})
|
||||
// The result is closer to zero than or equal to the dividend.
|
||||
public boolean modByKnownBoundsLimitedByDividendLowerInRange(int x, int y) {
|
||||
// d = ((char) y) + 1 => [1, 65536] divisor
|
||||
// e = ((byte) x) => [-128, 127]
|
||||
// e % d => [-128, 127]
|
||||
// in bounds, cannot optimize
|
||||
return ((byte) x) % (((char) y) + 1) <= -128;
|
||||
}
|
||||
|
||||
private static final int LIMIT_1 = INT_GEN.next();
|
||||
private static final int LIMIT_2 = INT_GEN.next();
|
||||
private static final int LIMIT_3 = INT_GEN.next();
|
||||
private static final int LIMIT_4 = INT_GEN.next();
|
||||
private static final int LIMIT_5 = INT_GEN.next();
|
||||
private static final int LIMIT_6 = INT_GEN.next();
|
||||
private static final int LIMIT_7 = INT_GEN.next();
|
||||
private static final int LIMIT_8 = INT_GEN.next();
|
||||
private static final Range RANGE_1 = Range.generate(INT_GEN);
|
||||
private static final Range RANGE_2 = Range.generate(INT_GEN);
|
||||
|
||||
@Test
|
||||
public int testRandomLimits(int x, int y) {
|
||||
x = RANGE_1.clamp(x);
|
||||
y = RANGE_2.clamp(y);
|
||||
int z = x % y;
|
||||
|
||||
int sum = 0;
|
||||
if (z < LIMIT_1) sum += 1;
|
||||
if (z < LIMIT_2) sum += 2;
|
||||
if (z < LIMIT_3) sum += 4;
|
||||
if (z < LIMIT_4) sum += 8;
|
||||
if (z > LIMIT_5) sum += 16;
|
||||
if (z > LIMIT_6) sum += 32;
|
||||
if (z > LIMIT_7) sum += 64;
|
||||
if (z > LIMIT_8) sum += 128;
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
@DontCompile
|
||||
public int testRandomLimitsInterpreted(int x, int y) {
|
||||
x = RANGE_1.clamp(x);
|
||||
y = RANGE_2.clamp(y);
|
||||
int z = x % y;
|
||||
|
||||
int sum = 0;
|
||||
if (z < LIMIT_1) sum += 1;
|
||||
if (z < LIMIT_2) sum += 2;
|
||||
if (z < LIMIT_3) sum += 4;
|
||||
if (z < LIMIT_4) sum += 8;
|
||||
if (z > LIMIT_5) sum += 16;
|
||||
if (z > LIMIT_6) sum += 32;
|
||||
if (z > LIMIT_7) sum += 64;
|
||||
if (z > LIMIT_8) sum += 128;
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
record Range(int lo, int hi) {
|
||||
Range {
|
||||
if (lo > hi) {
|
||||
throw new IllegalArgumentException("lo > hi");
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
int clamp(int v) {
|
||||
return Math.min(hi, Math.max(v, lo));
|
||||
}
|
||||
|
||||
static Range generate(Generator<Integer> g) {
|
||||
var a = g.next();
|
||||
var b = g.next();
|
||||
if (a > b) {
|
||||
var tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
return new Range(a, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
292
test/hotspot/jtreg/compiler/c2/gvn/ModLNodeValueTests.java
Normal file
292
test/hotspot/jtreg/compiler/c2/gvn/ModLNodeValueTests.java
Normal file
@ -0,0 +1,292 @@
|
||||
/*
|
||||
* 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.c2.gvn;
|
||||
|
||||
import compiler.lib.generators.Generator;
|
||||
import compiler.lib.generators.Generators;
|
||||
import compiler.lib.ir_framework.DontCompile;
|
||||
import compiler.lib.ir_framework.ForceInline;
|
||||
import compiler.lib.ir_framework.IR;
|
||||
import compiler.lib.ir_framework.IRNode;
|
||||
import compiler.lib.ir_framework.Run;
|
||||
import compiler.lib.ir_framework.Test;
|
||||
import compiler.lib.ir_framework.TestFramework;
|
||||
import jdk.test.lib.Asserts;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8356813
|
||||
* @summary Test that Value method of ModLNode is working as expected.
|
||||
* @key randomness
|
||||
* @library /test/lib /
|
||||
* @run driver compiler.c2.gvn.ModLNodeValueTests
|
||||
*/
|
||||
public class ModLNodeValueTests {
|
||||
private static final Generator<Long> LONG_GEN = Generators.G.longs();
|
||||
private static final long POS_LONG = Generators.G.longs().restricted(1L, Long.MAX_VALUE).next();
|
||||
private static final long NEG_LONG = Generators.G.longs().restricted(Long.MIN_VALUE, -1L).next();
|
||||
|
||||
public static void main(String[] args) {
|
||||
TestFramework.run();
|
||||
}
|
||||
|
||||
@Run(test = {
|
||||
"nonNegativeDividend", "nonNegativeDividendInRange",
|
||||
"negativeDividend", "negativeDividendInRange",
|
||||
"modByKnownBoundsUpper", "modByKnownBoundsUpperInRange",
|
||||
"modByKnownBoundsLower", "modByKnownBoundsLowerInRange",
|
||||
"modByKnownBoundsLimitedByDividendUpper", "modByKnownBoundsLimitedByDividendUpperInRange",
|
||||
"modByKnownBoundsLimitedByDividendLower", "modByKnownBoundsLimitedByDividendLowerInRange",
|
||||
"testRandomLimits"
|
||||
})
|
||||
public void runMethod() {
|
||||
long a = LONG_GEN.next();
|
||||
long b = LONG_GEN.next();
|
||||
|
||||
long min = Long.MIN_VALUE;
|
||||
long max = Long.MAX_VALUE;
|
||||
|
||||
assertResult(0, 0);
|
||||
assertResult(a, b);
|
||||
assertResult(min, min);
|
||||
assertResult(max, max);
|
||||
}
|
||||
|
||||
@DontCompile
|
||||
public void assertResult(long x, long y) {
|
||||
Asserts.assertEQ(x != 0 && POS_LONG % x < 0, nonNegativeDividend(x));
|
||||
Asserts.assertEQ(x != 0 && POS_LONG % x <= 0, nonNegativeDividendInRange(x));
|
||||
Asserts.assertEQ(x != 0 && NEG_LONG % x > 0, negativeDividend(x));
|
||||
Asserts.assertEQ(x != 0 && NEG_LONG % x >= 0, negativeDividendInRange(x));
|
||||
Asserts.assertEQ(x % (((byte) y) + 129L) > 255, modByKnownBoundsUpper(x, y));
|
||||
Asserts.assertEQ(x % (((byte) y) + 129L) >= 255, modByKnownBoundsUpperInRange(x, y));
|
||||
Asserts.assertEQ(x % (((byte) y) + 129L) < -255, modByKnownBoundsLower(x, y));
|
||||
Asserts.assertEQ(x % (((byte) y) + 129L) <= -255, modByKnownBoundsLowerInRange(x, y));
|
||||
Asserts.assertEQ(((byte) x) % (((char) y) + 1L) > 127, modByKnownBoundsLimitedByDividendUpper(x, y));
|
||||
Asserts.assertEQ(((byte) x) % (((char) y) + 1L) >= 127, modByKnownBoundsLimitedByDividendUpperInRange(x, y));
|
||||
Asserts.assertEQ(((byte) x) % (((char) y) + 1L) < -128, modByKnownBoundsLimitedByDividendLower(x, y));
|
||||
Asserts.assertEQ(((byte) x) % (((char) y) + 1L) <= -128, modByKnownBoundsLimitedByDividendLowerInRange(x, y));
|
||||
|
||||
int res;
|
||||
try {
|
||||
res = testRandomLimitsInterpreted(x, y);
|
||||
} catch (ArithmeticException _) {
|
||||
try {
|
||||
testRandomLimits(x, y);
|
||||
Asserts.fail("Expected ArithmeticException");
|
||||
return; // unreachable
|
||||
} catch (ArithmeticException _) {
|
||||
return; // test succeeded, no result to assert
|
||||
}
|
||||
}
|
||||
Asserts.assertEQ(res, testRandomLimits(x, y));
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MOD_L, IRNode.CMP_L})
|
||||
// The sign of the result of % is the same as the sign of the dividend,
|
||||
// i.e., posVal % x < 0 => false.
|
||||
public boolean nonNegativeDividend(long x) {
|
||||
return x != 0 && POS_LONG % x < 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_L, "1"})
|
||||
// The sign of the result of % is the same as the sign of the dividend,
|
||||
// i.e., posVal % x < 0 => false.
|
||||
// This uses <= to verify the % is not optimized away
|
||||
public boolean nonNegativeDividendInRange(long x) {
|
||||
return x != 0 && POS_LONG % x <= 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MOD_L, IRNode.CMP_L})
|
||||
// The sign of the result of % is the same as the sign of the dividend,
|
||||
// i.e., negValue % x > 0 => false.
|
||||
public boolean negativeDividend(long x) {
|
||||
return x != 0 && NEG_LONG % x > 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_L, "1"})
|
||||
// The sign of the result of % is the same as the sign of the dividend,
|
||||
// i.e., negValue % x > 0 => false.
|
||||
// This uses >= to verify the % is not optimized away
|
||||
public boolean negativeDividendInRange(long x) {
|
||||
return x != 0 && NEG_LONG % x >= 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MOD_L, IRNode.CMP_L})
|
||||
// The magnitude of the result is less than the divisor.
|
||||
public boolean modByKnownBoundsUpper(long x, long y) {
|
||||
// d = ((byte) y) + 129 => [1, 256] divisor
|
||||
// x % d => [-255, 255]
|
||||
return x % (((byte) y) + 129L) > 255;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_L, "1"})
|
||||
// The magnitude of the result is less than the divisor.
|
||||
public boolean modByKnownBoundsUpperInRange(long x, long y) {
|
||||
// d = ((byte) y) + 129 => [1, 256] divisor
|
||||
// x % d => [-255, 255]
|
||||
// in bounds, cannot optimize
|
||||
return x % (((byte) y) + 129L) >= 255;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MOD_L, IRNode.CMP_L})
|
||||
// The magnitude of the result is less than the divisor
|
||||
public boolean modByKnownBoundsLower(long x, long y) {
|
||||
// d = ((byte) y) + 129 => [1, 256] divisor
|
||||
// x % d => [-255, 255]
|
||||
return x % (((byte) y) + 129L) < -255;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_L, "1"})
|
||||
// The magnitude of the result is less than the divisor
|
||||
public boolean modByKnownBoundsLowerInRange(long x, long y) {
|
||||
// d = ((byte) y) + 129 => [1, 256] divisor
|
||||
// x % d => [-255, 255]
|
||||
// in bounds, cannot optimize
|
||||
return x % (((byte) y) + 129L) <= -255;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MOD_L, IRNode.CMP_L})
|
||||
// The result is closer to zero than or equal to the dividend.
|
||||
public boolean modByKnownBoundsLimitedByDividendUpper(long x, long y) {
|
||||
// d = ((char) y) + 1 => [1, 65536] divisor
|
||||
// e = ((byte) x) => [-128, 127]
|
||||
// e % d => [-128, 127]
|
||||
return ((byte) x) % (((char) y) + 1L) > 127;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_L, "1"})
|
||||
// The result is closer to zero than or equal to the dividend.
|
||||
public boolean modByKnownBoundsLimitedByDividendUpperInRange(long x, long y) {
|
||||
// d = ((char) y) + 1 => [1, 65536] divisor
|
||||
// e = ((byte) x) => [-128, 127]
|
||||
// e % d => [-128, 127]
|
||||
// in bounds, cannot optimize
|
||||
return ((byte) x) % (((char) y) + 1L) >= 127;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MOD_L, IRNode.CMP_L})
|
||||
// The result is closer to zero than or equal to the dividend.
|
||||
public boolean modByKnownBoundsLimitedByDividendLower(long x, long y) {
|
||||
// d = ((char) y) + 1 => [1, 65536] divisor
|
||||
// e = ((byte) x) => [-128, 127]
|
||||
// e % d => [-128, 127]
|
||||
return ((byte) x) % (((char) y) + 1L) < -128;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_L, "1"})
|
||||
// The result is closer to zero than or equal to the dividend.
|
||||
public boolean modByKnownBoundsLimitedByDividendLowerInRange(long x, long y) {
|
||||
// d = ((char) y) + 1 => [1, 65536] divisor
|
||||
// e = ((byte) x) => [-128, 127]
|
||||
// e % d => [-128, 127]
|
||||
// in bounds, cannot optimize
|
||||
return ((byte) x) % (((char) y) + 1L) <= -128;
|
||||
}
|
||||
|
||||
|
||||
private static final long LIMIT_1 = LONG_GEN.next();
|
||||
private static final long LIMIT_2 = LONG_GEN.next();
|
||||
private static final long LIMIT_3 = LONG_GEN.next();
|
||||
private static final long LIMIT_4 = LONG_GEN.next();
|
||||
private static final long LIMIT_5 = LONG_GEN.next();
|
||||
private static final long LIMIT_6 = LONG_GEN.next();
|
||||
private static final long LIMIT_7 = LONG_GEN.next();
|
||||
private static final long LIMIT_8 = LONG_GEN.next();
|
||||
private static final Range RANGE_1 = Range.generate(LONG_GEN);
|
||||
private static final Range RANGE_2 = Range.generate(LONG_GEN);
|
||||
|
||||
@Test
|
||||
public int testRandomLimits(long x, long y) {
|
||||
x = RANGE_1.clamp(x);
|
||||
y = RANGE_2.clamp(y);
|
||||
long z = x % y;
|
||||
|
||||
int sum = 0;
|
||||
if (z < LIMIT_1) sum += 1;
|
||||
if (z < LIMIT_2) sum += 2;
|
||||
if (z < LIMIT_3) sum += 4;
|
||||
if (z < LIMIT_4) sum += 8;
|
||||
if (z > LIMIT_5) sum += 16;
|
||||
if (z > LIMIT_6) sum += 32;
|
||||
if (z > LIMIT_7) sum += 64;
|
||||
if (z > LIMIT_8) sum += 128;
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
@DontCompile
|
||||
public int testRandomLimitsInterpreted(long x, long y) {
|
||||
x = RANGE_1.clamp(x);
|
||||
y = RANGE_2.clamp(y);
|
||||
long z = x % y;
|
||||
|
||||
int sum = 0;
|
||||
if (z < LIMIT_1) sum += 1;
|
||||
if (z < LIMIT_2) sum += 2;
|
||||
if (z < LIMIT_3) sum += 4;
|
||||
if (z < LIMIT_4) sum += 8;
|
||||
if (z > LIMIT_5) sum += 16;
|
||||
if (z > LIMIT_6) sum += 32;
|
||||
if (z > LIMIT_7) sum += 64;
|
||||
if (z > LIMIT_8) sum += 128;
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
record Range(long lo, long hi) {
|
||||
Range {
|
||||
if (lo > hi) {
|
||||
throw new IllegalArgumentException("lo > hi");
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
long clamp(long v) {
|
||||
return Math.min(hi, Math.max(v, lo));
|
||||
}
|
||||
|
||||
static Range generate(Generator<Long> g) {
|
||||
var a = g.next();
|
||||
var b = g.next();
|
||||
if (a > b) {
|
||||
var tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
return new Range(a, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user