diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index e149662e199..c78b92c6af5 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -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(r1->lo_as_long()) >> (jint)shift; + jint hi_verify = checked_cast(r1->hi_as_long()) >> (jint)shift; + assert((checked_cast(lo) == lo_verify) && (checked_cast(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); } //============================================================================= diff --git a/src/hotspot/share/opto/mulnode.hpp b/src/hotspot/share/opto/mulnode.hpp index 65a2cd112ec..b736c17b300 100644 --- a/src/hotspot/share/opto/mulnode.hpp +++ b/src/hotspot/share/opto/mulnode.hpp @@ -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; } }; diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 3efe4ca0825..562e090a41b 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -2064,6 +2064,7 @@ public: } Op_IL(Add) +Op_IL(And) Op_IL(Sub) Op_IL(Mul) Op_IL(URShift) diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 0369143c7b0..ad6d8b4112f 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -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); } } diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index fabcec48719..474145f2d06 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -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(); diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 6e17ef0db05..fe460cfb1d4 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -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); diff --git a/test/hotspot/jtreg/compiler/c2/irTests/RShiftINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/RShiftINodeIdealizationTests.java index e6501d3728a..bf500d42540 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/RShiftINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/RShiftINodeIdealizationTests.java @@ -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); + } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/RShiftLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/RShiftLNodeIdealizationTests.java index 0a8d2bde0b1..02ee8063948 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/RShiftLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/RShiftLNodeIdealizationTests.java @@ -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); + } }