8349361: C2: RShiftL should support all applicable transformations that RShiftI does

Reviewed-by: epeter, chagedorn, jkarthikeyan
This commit is contained in:
Roland Westrelin 2025-03-26 14:37:53 +00:00
parent eef6aefc21
commit 79bffe2f28
8 changed files with 317 additions and 137 deletions

View File

@ -225,6 +225,18 @@ MulNode* MulNode::make(Node* in1, Node* in2, BasicType bt) {
return nullptr;
}
MulNode* MulNode::make_and(Node* in1, Node* in2, BasicType bt) {
switch (bt) {
case T_INT:
return new AndINode(in1, in2);
case T_LONG:
return new AndLNode(in1, in2);
default:
fatal("Not implemented for %s", type2name(bt));
}
return nullptr;
}
//=============================================================================
//------------------------------Ideal------------------------------------------
@ -949,7 +961,7 @@ static bool const_shift_count(PhaseGVN* phase, Node* shiftNode, int* count) {
return false;
}
static int maskShiftAmount(PhaseGVN* phase, Node* shiftNode, int nBits) {
static int maskShiftAmount(PhaseGVN* phase, Node* shiftNode, uint nBits) {
int count = 0;
if (const_shift_count(phase, shiftNode, &count)) {
int maskedShift = count & (nBits - 1);
@ -1378,29 +1390,42 @@ const Type* LShiftLNode::Value(PhaseGVN* phase) const {
return TypeLong::make( (jlong)r1->get_con() << (jint)shift );
}
RShiftNode* RShiftNode::make(Node* in1, Node* in2, BasicType bt) {
switch (bt) {
case T_INT:
return new RShiftINode(in1, in2);
case T_LONG:
return new RShiftLNode(in1, in2);
default:
fatal("Not implemented for %s", type2name(bt));
}
return nullptr;
}
//=============================================================================
//------------------------------Identity---------------------------------------
Node* RShiftINode::Identity(PhaseGVN* phase) {
Node* RShiftNode::IdentityIL(PhaseGVN* phase, BasicType bt) {
int count = 0;
if (const_shift_count(phase, this, &count)) {
if ((count & (BitsPerJavaInteger - 1)) == 0) {
// Shift by a multiple of 32 does nothing
if ((count & (bits_per_java_integer(bt) - 1)) == 0) {
// Shift by a multiple of 32/64 does nothing
return in(1);
}
// Check for useless sign-masking
if (in(1)->Opcode() == Op_LShiftI &&
if (in(1)->Opcode() == Op_LShift(bt) &&
in(1)->req() == 3 &&
in(1)->in(2) == in(2)) {
count &= BitsPerJavaInteger-1; // semantics of Java shifts
count &= bits_per_java_integer(bt) - 1; // semantics of Java shifts
// Compute masks for which this shifting doesn't change
int lo = (-1 << (BitsPerJavaInteger - ((uint)count)-1)); // FFFF8000
int hi = ~lo; // 00007FFF
const TypeInt* t11 = phase->type(in(1)->in(1))->isa_int();
jlong lo = (CONST64(-1) << (bits_per_java_integer(bt) - ((uint)count)-1)); // FFFF8000
jlong hi = ~lo; // 00007FFF
const TypeInteger* t11 = phase->type(in(1)->in(1))->isa_integer(bt);
if (t11 == nullptr) {
return this;
}
// Does actual value fit inside of mask?
if (lo <= t11->_lo && t11->_hi <= hi) {
if (lo <= t11->lo_as_long() && t11->hi_as_long() <= hi) {
return in(1)->in(1); // Then shifting is a nop
}
}
@ -1408,39 +1433,62 @@ Node* RShiftINode::Identity(PhaseGVN* phase) {
return this;
}
//------------------------------Ideal------------------------------------------
Node *RShiftINode::Ideal(PhaseGVN *phase, bool can_reshape) {
Node* RShiftINode::Identity(PhaseGVN* phase) {
return IdentityIL(phase, T_INT);
}
Node* RShiftNode::IdealIL(PhaseGVN* phase, bool can_reshape, BasicType bt) {
// Inputs may be TOP if they are dead.
const TypeInt *t1 = phase->type(in(1))->isa_int();
if (!t1) return nullptr; // Left input is an integer
const TypeInt *t3; // type of in(1).in(2)
int shift = maskShiftAmount(phase, this, BitsPerJavaInteger);
const TypeInteger* t1 = phase->type(in(1))->isa_integer(bt);
if (t1 == nullptr) {
return NodeSentinel; // Left input is an integer
}
int shift = maskShiftAmount(phase, this, bits_per_java_integer(bt));
if (shift == 0) {
return nullptr;
return NodeSentinel;
}
// Check for (x & 0xFF000000) >> 24, whose mask can be made smaller.
// and convert to (x >> 24) & (0xFF000000 >> 24) = x >> 24
// Such expressions arise normally from shift chains like (byte)(x >> 24).
const Node *mask = in(1);
if( mask->Opcode() == Op_AndI &&
(t3 = phase->type(mask->in(2))->isa_int()) &&
t3->is_con() ) {
Node *x = mask->in(1);
jint maskbits = t3->get_con();
// Convert to "(x >> shift) & (mask >> shift)"
Node *shr_nomask = phase->transform( new RShiftINode(mask->in(1), in(2)) );
return new AndINode(shr_nomask, phase->intcon( maskbits >> shift));
const Node* and_node = in(1);
if (and_node->Opcode() != Op_And(bt)) {
return nullptr;
}
const TypeInteger* mask_t = phase->type(and_node->in(2))->isa_integer(bt);
if (mask_t != nullptr && mask_t->is_con()) {
jlong maskbits = mask_t->get_con_as_long(bt);
// Convert to "(x >> shift) & (mask >> shift)"
Node* shr_nomask = phase->transform(RShiftNode::make(and_node->in(1), in(2), bt));
return MulNode::make_and(shr_nomask, phase->integercon(maskbits >> shift, bt), bt);
}
return nullptr;
}
Node* RShiftINode::Ideal(PhaseGVN* phase, bool can_reshape) {
Node* progress = IdealIL(phase, can_reshape, T_INT);
if (progress == NodeSentinel) {
return nullptr;
}
if (progress != nullptr) {
return progress;
}
int shift = maskShiftAmount(phase, this, BitsPerJavaInteger);
assert(shift != 0, "handled by IdealIL");
// Check for "(short[i] <<16)>>16" which simply sign-extends
const Node *shl = in(1);
if( shl->Opcode() != Op_LShiftI ) return nullptr;
if (shl->Opcode() != Op_LShiftI) {
return nullptr;
}
if( shift == 16 &&
(t3 = phase->type(shl->in(2))->isa_int()) &&
t3->is_con(16) ) {
const TypeInt* left_shift_t = phase->type(shl->in(2))->isa_int();
if (left_shift_t == nullptr) {
return nullptr;
}
if (shift == 16 && left_shift_t->is_con(16)) {
Node *ld = shl->in(1);
if( ld->Opcode() == Op_LoadS ) {
if (ld->Opcode() == Op_LoadS) {
// Sign extension is just useless here. Return a RShiftI of zero instead
// returning 'ld' directly. We cannot return an old Node directly as
// that is the job of 'Identity' calls and Identity calls only work on
@ -1458,9 +1506,7 @@ Node *RShiftINode::Ideal(PhaseGVN *phase, bool can_reshape) {
}
// Check for "(byte[i] <<24)>>24" which simply sign-extends
if( shift == 24 &&
(t3 = phase->type(shl->in(2))->isa_int()) &&
t3->is_con(24) ) {
if (shift == 24 && left_shift_t->is_con(24)) {
Node *ld = shl->in(1);
if (ld->Opcode() == Op_LoadB) {
// Sign extension is just useless here
@ -1473,46 +1519,66 @@ Node *RShiftINode::Ideal(PhaseGVN *phase, bool can_reshape) {
return nullptr;
}
//------------------------------Value------------------------------------------
// A RShiftINode shifts its input2 right by input1 amount.
const Type* RShiftINode::Value(PhaseGVN* phase) const {
const Type *t1 = phase->type( in(1) );
const Type *t2 = phase->type( in(2) );
const Type* RShiftNode::ValueIL(PhaseGVN* phase, BasicType bt) const {
const Type* t1 = phase->type(in(1));
const Type* t2 = phase->type(in(2));
// Either input is TOP ==> the result is TOP
if( t1 == Type::TOP ) return Type::TOP;
if( t2 == Type::TOP ) return Type::TOP;
if (t1 == Type::TOP) {
return Type::TOP;
}
if (t2 == Type::TOP) {
return Type::TOP;
}
// Left input is ZERO ==> the result is ZERO.
if( t1 == TypeInt::ZERO ) return TypeInt::ZERO;
if (t1 == TypeInteger::zero(bt)) {
return TypeInteger::zero(bt);
}
// Shift by zero does nothing
if( t2 == TypeInt::ZERO ) return t1;
if (t2 == TypeInt::ZERO) {
return t1;
}
// Either input is BOTTOM ==> the result is BOTTOM
if (t1 == Type::BOTTOM || t2 == Type::BOTTOM)
return TypeInt::INT;
if (t1 == Type::BOTTOM || t2 == Type::BOTTOM) {
return TypeInteger::bottom(bt);
}
const TypeInt *r1 = t1->is_int(); // Handy access
const TypeInt *r2 = t2->is_int(); // Handy access
const TypeInteger* r1 = t1->isa_integer(bt);
const TypeInt* r2 = t2->isa_int();
// If the shift is a constant, just shift the bounds of the type.
// For example, if the shift is 31, we just propagate sign bits.
// For example, if the shift is 31/63, we just propagate sign bits.
if (!r1->is_con() && r2->is_con()) {
uint shift = r2->get_con();
shift &= BitsPerJavaInteger-1; // semantics of Java shifts
// Shift by a multiple of 32 does nothing:
if (shift == 0) return t1;
shift &= bits_per_java_integer(bt) - 1; // semantics of Java shifts
// Shift by a multiple of 32/64 does nothing:
if (shift == 0) {
return t1;
}
// Calculate reasonably aggressive bounds for the result.
// This is necessary if we are to correctly type things
// like (x<<24>>24) == ((byte)x).
jint lo = (jint)r1->_lo >> (jint)shift;
jint hi = (jint)r1->_hi >> (jint)shift;
jlong lo = r1->lo_as_long() >> (jint)shift;
jlong hi = r1->hi_as_long() >> (jint)shift;
assert(lo <= hi, "must have valid bounds");
const TypeInt* ti = TypeInt::make(lo, hi, MAX2(r1->_widen,r2->_widen));
#ifdef ASSERT
if (bt == T_INT) {
jint lo_verify = checked_cast<jint>(r1->lo_as_long()) >> (jint)shift;
jint hi_verify = checked_cast<jint>(r1->hi_as_long()) >> (jint)shift;
assert((checked_cast<jint>(lo) == lo_verify) && (checked_cast<jint>(hi) == hi_verify), "inconsistent");
}
#endif
const TypeInteger* ti = TypeInteger::make(lo, hi, MAX2(r1->_widen,r2->_widen), bt);
#ifdef ASSERT
// Make sure we get the sign-capture idiom correct.
if (shift == BitsPerJavaInteger-1) {
if (r1->_lo >= 0) assert(ti == TypeInt::ZERO, ">>31 of + is 0");
if (r1->_hi < 0) assert(ti == TypeInt::MINUS_1, ">>31 of - is -1");
if (shift == bits_per_java_integer(bt) - 1) {
if (r1->lo_as_long() >= 0) {
assert(ti == TypeInteger::zero(bt), ">>31/63 of + is 0");
}
if (r1->hi_as_long() < 0) {
assert(ti == TypeInteger::minus_1(bt), ">>31/63 of - is -1");
}
}
#endif
return ti;
@ -1520,89 +1586,42 @@ const Type* RShiftINode::Value(PhaseGVN* phase) const {
if (!r1->is_con() || !r2->is_con()) {
// If the left input is non-negative the result must also be non-negative, regardless of what the right input is.
if (r1->_lo >= 0) {
return TypeInt::make(0, r1->_hi, MAX2(r1->_widen, r2->_widen));
if (r1->lo_as_long() >= 0) {
return TypeInteger::make(0, r1->hi_as_long(), MAX2(r1->_widen, r2->_widen), bt);
}
// Conversely, if the left input is negative then the result must be negative.
if (r1->_hi <= -1) {
return TypeInt::make(r1->_lo, -1, MAX2(r1->_widen, r2->_widen));
if (r1->hi_as_long() <= -1) {
return TypeInteger::make(r1->lo_as_long(), -1, MAX2(r1->_widen, r2->_widen), bt);
}
return TypeInt::INT;
return TypeInteger::bottom(bt);
}
// Signed shift right
return TypeInt::make(r1->get_con() >> (r2->get_con() & 31));
return TypeInteger::make(r1->get_con_as_long(bt) >> (r2->get_con() & (bits_per_java_integer(bt) - 1)), bt);
}
const Type* RShiftINode::Value(PhaseGVN* phase) const {
return ValueIL(phase, T_INT);
}
//=============================================================================
//------------------------------Identity---------------------------------------
Node* RShiftLNode::Identity(PhaseGVN* phase) {
const TypeInt *ti = phase->type(in(2))->isa_int(); // Shift count is an int.
return (ti && ti->is_con() && (ti->get_con() & (BitsPerJavaLong - 1)) == 0) ? in(1) : this;
return IdentityIL(phase, T_LONG);
}
Node* RShiftLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
Node* progress = IdealIL(phase, can_reshape, T_LONG);
if (progress == NodeSentinel) {
return nullptr;
}
return progress;
}
//------------------------------Value------------------------------------------
// A RShiftLNode shifts its input2 right by input1 amount.
const Type* RShiftLNode::Value(PhaseGVN* phase) const {
const Type *t1 = phase->type( in(1) );
const Type *t2 = phase->type( in(2) );
// Either input is TOP ==> the result is TOP
if( t1 == Type::TOP ) return Type::TOP;
if( t2 == Type::TOP ) return Type::TOP;
// Left input is ZERO ==> the result is ZERO.
if( t1 == TypeLong::ZERO ) return TypeLong::ZERO;
// Shift by zero does nothing
if( t2 == TypeInt::ZERO ) return t1;
// Either input is BOTTOM ==> the result is BOTTOM
if (t1 == Type::BOTTOM || t2 == Type::BOTTOM)
return TypeLong::LONG;
const TypeLong *r1 = t1->is_long(); // Handy access
const TypeInt *r2 = t2->is_int (); // Handy access
// If the shift is a constant, just shift the bounds of the type.
// For example, if the shift is 63, we just propagate sign bits.
if (!r1->is_con() && r2->is_con()) {
uint shift = r2->get_con();
shift &= (2*BitsPerJavaInteger)-1; // semantics of Java shifts
// Shift by a multiple of 64 does nothing:
if (shift == 0) return t1;
// Calculate reasonably aggressive bounds for the result.
// This is necessary if we are to correctly type things
// like (x<<24>>24) == ((byte)x).
jlong lo = (jlong)r1->_lo >> (jlong)shift;
jlong hi = (jlong)r1->_hi >> (jlong)shift;
assert(lo <= hi, "must have valid bounds");
const TypeLong* tl = TypeLong::make(lo, hi, MAX2(r1->_widen,r2->_widen));
#ifdef ASSERT
// Make sure we get the sign-capture idiom correct.
if (shift == (2*BitsPerJavaInteger)-1) {
if (r1->_lo >= 0) assert(tl == TypeLong::ZERO, ">>63 of + is 0");
if (r1->_hi < 0) assert(tl == TypeLong::MINUS_1, ">>63 of - is -1");
}
#endif
return tl;
}
if (!r1->is_con() || !r2->is_con()) {
// If the left input is non-negative the result must also be non-negative, regardless of what the right input is.
if (r1->_lo >= 0) {
return TypeLong::make(0, r1->_hi, MAX2(r1->_widen, r2->_widen));
}
// Conversely, if the left input is negative then the result must be negative.
if (r1->_hi <= -1) {
return TypeLong::make(r1->_lo, -1, MAX2(r1->_widen, r2->_widen));
}
return TypeLong::LONG;
}
return TypeLong::make(r1->get_con() >> (r2->get_con() & 63));
return ValueIL(phase, T_LONG);
}
//=============================================================================

View File

@ -82,6 +82,7 @@ public:
virtual int min_opcode() const = 0;
static MulNode* make(Node* in1, Node* in2, BasicType bt);
static MulNode* make_and(Node* in1, Node* in2, BasicType bt);
protected:
Node* AndIL_sum_and_mask(PhaseGVN* phase, BasicType bt);
@ -318,28 +319,42 @@ class RotateRightNode : public TypeNode {
virtual const Type* Value(PhaseGVN* phase) const;
};
class RShiftNode : public Node {
public:
RShiftNode(Node* in1, Node* in2) : Node(nullptr, in1, in2) {}
Node* IdealIL(PhaseGVN* phase, bool can_reshape, BasicType bt);
Node* IdentityIL(PhaseGVN* phase, BasicType bt);
const Type* ValueIL(PhaseGVN* phase, BasicType bt) const;
static RShiftNode* make(Node* in1, Node* in2, BasicType bt);
};
//------------------------------RShiftINode------------------------------------
// Signed shift right
class RShiftINode : public Node {
class RShiftINode : public RShiftNode {
public:
RShiftINode( Node *in1, Node *in2 ) : Node(nullptr,in1,in2) {}
RShiftINode(Node* in1, Node* in2) : RShiftNode(in1, in2) {}
virtual int Opcode() const;
virtual Node* Identity(PhaseGVN* phase);
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
virtual const Type* Value(PhaseGVN* phase) const;
const Type *bottom_type() const { return TypeInt::INT; }
const Type* bottom_type() const { return TypeInt::INT; }
virtual uint ideal_reg() const { return Op_RegI; }
};
//------------------------------RShiftLNode------------------------------------
// Signed shift right
class RShiftLNode : public Node {
class RShiftLNode : public RShiftNode {
public:
RShiftLNode( Node *in1, Node *in2 ) : Node(nullptr,in1,in2) {}
RShiftLNode(Node* in1, Node* in2) : RShiftNode(in1,in2) {}
virtual int Opcode() const;
virtual Node* Identity(PhaseGVN* phase);
virtual Node* Ideal(PhaseGVN *phase, bool can_reshape);
virtual const Type* Value(PhaseGVN* phase) const;
const Type *bottom_type() const { return TypeLong::LONG; }
const Type* bottom_type() const { return TypeLong::LONG; }
virtual uint ideal_reg() const { return Op_RegL; }
};

View File

@ -2064,6 +2064,7 @@ public:
}
Op_IL(Add)
Op_IL(And)
Op_IL(Sub)
Op_IL(Mul)
Op_IL(URShift)

View File

@ -1611,10 +1611,10 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_
use->visit_uses(push_the_uses_to_worklist, is_boundary);
}
// If changed LShift inputs, check RShift users for useless sign-ext
if( use_op == Op_LShiftI ) {
if (use_op == Op_LShiftI || use_op == Op_LShiftL) {
for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) {
Node* u = use->fast_out(i2);
if (u->Opcode() == Op_RShiftI)
if (u->Opcode() == Op_RShiftI || u->Opcode() == Op_RShiftL)
worklist.push(u);
}
}

View File

@ -1699,6 +1699,10 @@ const TypeInteger* TypeInteger::make(jlong lo, jlong hi, int w, BasicType bt) {
return TypeLong::make(lo, hi, w);
}
const TypeInteger* TypeInteger::make(jlong con, BasicType bt) {
return make(con, con, WidenMin, bt);
}
jlong TypeInteger::get_con_as_long(BasicType bt) const {
if (bt == T_INT) {
return is_int()->get_con();

View File

@ -606,6 +606,7 @@ public:
virtual short widen_limit() const { return _widen; }
static const TypeInteger* make(jlong lo, jlong hi, int w, BasicType bt);
static const TypeInteger* make(jlong con, BasicType bt);
static const TypeInteger* bottom(BasicType type);
static const TypeInteger* zero(BasicType type);

View File

@ -27,7 +27,7 @@ import compiler.lib.ir_framework.*;
/*
* @test
* @bug 8320330
* @bug 8320330 8349361
* @summary Test that RShiftINode optimizations are being performed as expected.
* @library /test/lib /
* @run driver compiler.c2.irTests.RShiftINodeIdealizationTests
@ -37,7 +37,7 @@ public class RShiftINodeIdealizationTests {
TestFramework.run();
}
@Run(test = { "test1", "test2", "test3", "test4" })
@Run(test = { "test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8", "test9", "test10" })
public void runMethod() {
int a = RunInfo.getRandom().nextInt();
int b = RunInfo.getRandom().nextInt();
@ -56,6 +56,10 @@ public class RShiftINodeIdealizationTests {
assertResult(max, min);
assertResult(min, min);
assertResult(max, max);
assertResult(test7Min, b);
assertResult(test7Max, b);
assertResult(test7Min-1, b);
assertResult(test7Max+1, b);
}
@DontCompile
@ -64,6 +68,15 @@ public class RShiftINodeIdealizationTests {
Asserts.assertEQ(((x & 127) >> y) >= 0 ? 0 : 1, test2(x, y));
Asserts.assertEQ(((-(x & 127) - 1) >> y) >= 0 ? 0 : 1, test3(x, y));
Asserts.assertEQ((x >> 30) > 4 ? 0 : 1, test4(x, y));
Asserts.assertEQ((x & test5Mask) >> test5Shift, test5(x));
Asserts.assertEQ(x, test6(x));
int x7 = Integer.max(Integer.min(x, test7Max), test7Min);
Asserts.assertEQ(((x7 << test7Shift) >> test7Shift), test7(x));
int x8 = Integer.max(Integer.min(x, test7Max+1), test7Min);
Asserts.assertEQ((x8 << test7Shift) >> test7Shift, test8(x));
int x9 = Integer.max(Integer.min(x, test7Max), test7Min-1);
Asserts.assertEQ((x9 << test7Shift) >> test7Shift, test9(x));
Asserts.assertEQ((x7 << test7Shift) >> test10Shift, test10(x));
}
@Test
@ -89,4 +102,67 @@ public class RShiftINodeIdealizationTests {
public int test4(int x, int y) {
return (x >> 30) > 4 ? 0 : 1;
}
final static int test5Shift = RunInfo.getRandom().nextInt(1, 32);
final static int test5Mask = -1 << test5Shift;
@Test
@IR(counts = { IRNode.RSHIFT_I, "1" })
@IR(failOn = { IRNode.AND_I })
public int test5(int x) {
return (x & test5Mask) >> test5Shift;
}
final static int test6Shift = RunInfo.getRandom().nextInt(Integer.MAX_VALUE / 32) * 32 ;
@Test
@IR(failOn = { IRNode.RSHIFT_I })
public int test6(int x) {
return (x >> test6Shift);
}
// test that (x << shift) >> shift) is a nop if upper bits of x
// that are shifted left and then right + one bit are all ones or
// zeroes. For instance:
// shift = 15, min = 0xffff0000, max=0x0000ffff
// min << shift = 0x80000000, (min << shift) >> shift = 0xffff0000
// (min+1) << shift = 0x80008000, ((min+1) << shift) >> shift = 0xffff0001
// (max-1) << shift = 0x7fff0000, ((max-1) << shift) >> shift = 0x0000fffe
// max << shift = 0x7fff8000, (min << shift) >> shift = 0x0000ffff
// But:
// (min-1) << shift = 7fff8000, ((min-1) << shift) >> shift = 0x0000ffff != 0xfffeffff
// (max+1) << shift = 0x80000000, ((max+1) << shift) >> shift = 0xffff0000 != 0x00010000
final static int test7Shift = RunInfo.getRandom().nextInt(1, 32);
final static int test7Min = -1 << (32 - test7Shift - 1);
final static int test7Max = ~test7Min;
@Test
@IR(failOn = { IRNode.RSHIFT_I, IRNode.LSHIFT_I })
public int test7(int x) {
x = Integer.max(Integer.min(x, test7Max), test7Min);
return ((x << test7Shift) >> test7Shift);
}
@Test
@IR(counts = { IRNode.RSHIFT_I, "1", IRNode.LSHIFT_I, "1" })
public int test8(int x) {
x = Integer.max(Integer.min(x, test7Max+1), test7Min);
return ((x << test7Shift) >> test7Shift);
}
@Test
@IR(counts = { IRNode.RSHIFT_I, "1", IRNode.LSHIFT_I, "1" })
public int test9(int x) {
x = Integer.max(Integer.min(x, test7Max), test7Min-1);
return ((x << test7Shift) >> test7Shift);
}
final static int test10Shift = RunInfo.getRandom().nextInt(32);
@Test
public int test10(int x) {
x = Integer.max(Integer.min(x, test7Max), test7Min);
return ((x << test7Shift) >> test10Shift);
}
}

View File

@ -27,7 +27,7 @@ import compiler.lib.ir_framework.*;
/*
* @test
* @bug 8320330
* @bug 8320330 8349361
* @summary Test that RShiftLNode optimizations are being performed as expected.
* @library /test/lib /
* @run driver compiler.c2.irTests.RShiftLNodeIdealizationTests
@ -37,7 +37,7 @@ public class RShiftLNodeIdealizationTests {
TestFramework.run();
}
@Run(test = { "test1", "test2", "test3", "test4" })
@Run(test = { "test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8", "test9", "test10" })
public void runMethod() {
long a = RunInfo.getRandom().nextLong();
long b = RunInfo.getRandom().nextLong();
@ -56,6 +56,10 @@ public class RShiftLNodeIdealizationTests {
assertResult(max, min);
assertResult(min, min);
assertResult(max, max);
assertResult(test7Min, b);
assertResult(test7Max, b);
assertResult(test7Min-1, b);
assertResult(test7Max+1, b);
}
@DontCompile
@ -64,6 +68,15 @@ public class RShiftLNodeIdealizationTests {
Asserts.assertEQ(((x & 127) >> y) >= 0 ? 0L : 1L, test2(x, y));
Asserts.assertEQ(((-(x & 127) - 1) >> y) >= 0 ? 0L : 1L, test3(x, y));
Asserts.assertEQ((x >> 62) > 4 ? 0L : 1L, test4(x, y));
Asserts.assertEQ((x & test5Mask) >> test5Shift, test5(x));
Asserts.assertEQ(x, test6(x));
long x7 = Long.max(Long.min(x, test7Max), test7Min);
Asserts.assertEQ(((x7 << test7Shift) >> test7Shift), test7(x));
long x8 = Long.max(Long.min(x, test7Max+1), test7Min);
Asserts.assertEQ((x8 << test7Shift) >> test7Shift, test8(x));
long x9 = Long.max(Long.min(x, test7Max), test7Min-1);
Asserts.assertEQ((x9 << test7Shift) >> test7Shift, test9(x));
Asserts.assertEQ(((x7 << test7Shift) >> test10Shift), test10(x));
}
@Test
@ -89,4 +102,55 @@ public class RShiftLNodeIdealizationTests {
public long test4(long x, long y) {
return (x >> 62) > 4 ? 0L : 1L;
}
final static int test5Shift = RunInfo.getRandom().nextInt(1, 64);
final static long test5Mask = -1L << test5Shift;
@Test
@IR(counts = { IRNode.RSHIFT_L, "1" })
@IR(failOn = { IRNode.AND_L })
public long test5(long x) {
return (x & test5Mask) >> test5Shift;
}
final static int test6Shift = RunInfo.getRandom().nextInt(Integer.MAX_VALUE / 64) * 64 ;
@Test
@IR(failOn = { IRNode.RSHIFT_L })
public long test6(long x) {
return (x >> test6Shift);
}
// See comment in RShiftINodeIdealizationTests
final static int test7Shift = RunInfo.getRandom().nextInt(1, 64);
final static long test7Min = -1L << (64 - test7Shift - 1);
final static long test7Max = ~test7Min;
@Test
@IR(failOn = { IRNode.RSHIFT_L, IRNode.LSHIFT_L })
public long test7(long x) {
x = Long.max(Long.min(x, test7Max), test7Min);
return ((x << test7Shift) >> test7Shift);
}
@Test
@IR(counts = { IRNode.RSHIFT_L, "1", IRNode.LSHIFT_L, "1" })
public long test8(long x) {
x = Long.max(Long.min(x, test7Max+1), test7Min);
return ((x << test7Shift) >> test7Shift);
}
@Test
@IR(counts = { IRNode.RSHIFT_L, "1", IRNode.LSHIFT_L, "1" })
public long test9(long x) {
x = Long.max(Long.min(x, test7Max), test7Min-1);
return ((x << test7Shift) >> test7Shift);
}
final static int test10Shift = RunInfo.getRandom().nextInt(64);
@Test
public long test10(long x) {
x = Long.max(Long.min(x, test7Max), test7Min);
return ((x << test7Shift) >> test10Shift);
}
}