diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index ad98fda025f07..e9389960cf655 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -670,9 +670,13 @@ const Type *AndINode::mul_ring( const Type *t0, const Type *t1 ) const { return and_value(r0, r1); } +// Is expr a neutral element wrt addition under mask? +static bool AndIL_is_zero_element(const PhaseGVN* phase, const Node* expr, const Node* mask, BasicType bt); + const Type* AndINode::Value(PhaseGVN* phase) const { // patterns similar to (v << 2) & 3 - if (AndIL_shift_and_mask_is_always_zero(phase, in(1), in(2), T_INT, true)) { + if (AndIL_is_zero_element(phase, in(1), in(2), T_INT) || + AndIL_is_zero_element(phase, in(2), in(1), T_INT)) { return TypeInt::ZERO; } @@ -719,7 +723,7 @@ Node* AndINode::Identity(PhaseGVN* phase) { //------------------------------Ideal------------------------------------------ Node *AndINode::Ideal(PhaseGVN *phase, bool can_reshape) { // pattern similar to (v1 + (v2 << 2)) & 3 transformed to v1 & 3 - Node* progress = AndIL_add_shift_and_mask(phase, T_INT); + Node* progress = AndIL_sum_and_mask(phase, T_INT); if (progress != nullptr) { return progress; } @@ -803,7 +807,8 @@ const Type *AndLNode::mul_ring( const Type *t0, const Type *t1 ) const { const Type* AndLNode::Value(PhaseGVN* phase) const { // patterns similar to (v << 2) & 3 - if (AndIL_shift_and_mask_is_always_zero(phase, in(1), in(2), T_LONG, true)) { + if (AndIL_is_zero_element(phase, in(1), in(2), T_LONG) || + AndIL_is_zero_element(phase, in(2), in(1), T_LONG)) { return TypeLong::ZERO; } @@ -851,7 +856,7 @@ Node* AndLNode::Identity(PhaseGVN* phase) { //------------------------------Ideal------------------------------------------ Node *AndLNode::Ideal(PhaseGVN *phase, bool can_reshape) { // pattern similar to (v1 + (v2 << 2)) & 3 transformed to v1 & 3 - Node* progress = AndIL_add_shift_and_mask(phase, T_LONG); + Node* progress = AndIL_sum_and_mask(phase, T_LONG); if (progress != nullptr) { return progress; } @@ -2052,99 +2057,70 @@ const Type* RotateRightNode::Value(PhaseGVN* phase) const { } } -// Given an expression (AndX shift mask) or (AndX mask shift), -// determine if the AndX must always produce zero, because the -// the shift (x<uncast(); + const TypeInteger* type = phase->type(expr)->isa_integer(bt); + if (type == nullptr) { + return 0; + } + + if (type->is_con()) { + long con = type->get_con_as_long(bt); + return con == 0L ? (type2aelembytes(bt) * BitsPerByte) : count_trailing_zeros(con); + } + + if (expr->Opcode() == Op_ConvI2L) { + expr = expr->in(1)->uncast(); + bt = T_INT; + type = phase->type(expr)->isa_int(); + } + + if (expr->Opcode() == Op_LShift(bt)) { + const TypeInt* rhs_t = phase->type(expr->in(2))->isa_int(); + if (rhs_t == nullptr || !rhs_t->is_con()) { + return 0; + } + return rhs_t->get_con() % (type2aelembytes(bt) * BitsPerByte); + } + + return 0; +} + +// Given an expression (AndX T+expr mask), determine +// whether expr is neutral wrt addition under mask +// and hence the result is always equivalent to (AndX T mask), // The X in AndX must be I or L, depending on bt. -// Specifically, the following cases fold to zero, +// Specifically, this holds for the following cases, // when the shift value N is large enough to zero out // all the set positions of the and-mask M. // (AndI (LShiftI _ #N) #M) => #0 // (AndL (LShiftL _ #N) #M) => #0 // (AndL (ConvI2L (LShiftI _ #N)) #M) => #0 +// as well as for constant operands: +// (AndI (ConI [+-] _ << #N) #M) => #0 +// (AndL (ConL [+-] _ << #N) #M) => #0 // The M and N values must satisfy ((-1 << N) & M) == 0. -// Because the optimization might work for a non-constant -// mask M, we check the AndX for both operand orders. -bool MulNode::AndIL_shift_and_mask_is_always_zero(PhaseGVN* phase, Node* shift, Node* mask, BasicType bt, bool check_reverse) { - if (mask == nullptr || shift == nullptr) { - return false; - } +static bool AndIL_is_zero_element(const PhaseGVN* phase, const Node* expr, const Node* mask, BasicType bt) { const TypeInteger* mask_t = phase->type(mask)->isa_integer(bt); - if (mask_t == nullptr || phase->type(shift)->isa_integer(bt) == nullptr) { + if (mask_t == nullptr) { return false; } - shift = shift->uncast(); - if (shift == nullptr) { - return false; - } - if (phase->type(shift)->isa_integer(bt) == nullptr) { - return false; - } - BasicType shift_bt = bt; - if (bt == T_LONG && shift->Opcode() == Op_ConvI2L) { - bt = T_INT; - Node* val = shift->in(1); - if (val == nullptr) { - return false; - } - val = val->uncast(); - if (val == nullptr) { - return false; - } - if (val->Opcode() == Op_LShiftI) { - shift_bt = T_INT; - shift = val; - if (phase->type(shift)->isa_integer(bt) == nullptr) { - return false; - } - } - } - if (shift->Opcode() != Op_LShift(shift_bt)) { - if (check_reverse && - (mask->Opcode() == Op_LShift(bt) || - (bt == T_LONG && mask->Opcode() == Op_ConvI2L))) { - // try it the other way around - return AndIL_shift_and_mask_is_always_zero(phase, mask, shift, bt, false); - } - return false; - } - Node* shift2 = shift->in(2); - if (shift2 == nullptr) { - return false; - } - const Type* shift2_t = phase->type(shift2); - if (!shift2_t->isa_int() || !shift2_t->is_int()->is_con()) { - return false; - } - - jint shift_con = shift2_t->is_int()->get_con() & ((shift_bt == T_INT ? BitsPerJavaInteger : BitsPerJavaLong) - 1); - if ((((jlong)1) << shift_con) > mask_t->hi_as_long() && mask_t->lo_as_long() >= 0) { - return true; - } - return false; + jint zeros = AndIL_min_trailing_zeros(phase, expr, bt); + return zeros > 0 && ((((jlong)1) << zeros) > mask_t->hi_as_long() && mask_t->lo_as_long() >= 0); } -// Given an expression (AndX (AddX v1 (LShiftX v2 #N)) #M) -// determine if the AndX must always produce (AndX v1 #M), -// because the shift (v2< (AndI v1 #M) -// (AndL (AddI v1 (LShiftL _ #N)) #M) => (AndL v1 #M) -// (AndL (AddL v1 (ConvI2L (LShiftI _ #N))) #M) => (AndL v1 #M) -// The M and N values must satisfy ((-1 << N) & M) == 0. -// Because the optimization might work for a non-constant -// mask M, and because the AddX operands can come in either -// order, we check for every operand order. -Node* MulNode::AndIL_add_shift_and_mask(PhaseGVN* phase, BasicType bt) { +// Given an expression (AndX (AddX v1 v2) mask) +// determine if the AndX must always produce (AndX v1 mask), +// because v2 is zero wrt addition under mask. +// Because the AddX operands can come in either +// order, we check for both orders. +Node* MulNode::AndIL_sum_and_mask(PhaseGVN* phase, BasicType bt) { Node* add = in(1); Node* mask = in(2); - if (add == nullptr || mask == nullptr) { - return nullptr; - } int addidx = 0; if (add->Opcode() == Op_Add(bt)) { addidx = 1; @@ -2156,14 +2132,12 @@ Node* MulNode::AndIL_add_shift_and_mask(PhaseGVN* phase, BasicType bt) { if (addidx > 0) { Node* add1 = add->in(1); Node* add2 = add->in(2); - if (add1 != nullptr && add2 != nullptr) { - if (AndIL_shift_and_mask_is_always_zero(phase, add1, mask, bt, false)) { - set_req_X(addidx, add2, phase); - return this; - } else if (AndIL_shift_and_mask_is_always_zero(phase, add2, mask, bt, false)) { - set_req_X(addidx, add1, phase); - return this; - } + if (AndIL_is_zero_element(phase, add1, mask, bt)) { + set_req_X(addidx, add2, phase); + return this; + } else if (AndIL_is_zero_element(phase, add2, mask, bt)) { + set_req_X(addidx, add1, phase); + return this; } } return nullptr; diff --git a/src/hotspot/share/opto/mulnode.hpp b/src/hotspot/share/opto/mulnode.hpp index c8d168685d9e5..0eec6dd10c019 100644 --- a/src/hotspot/share/opto/mulnode.hpp +++ b/src/hotspot/share/opto/mulnode.hpp @@ -83,8 +83,8 @@ class MulNode : public Node { static MulNode* make(Node* in1, Node* in2, BasicType bt); - static bool AndIL_shift_and_mask_is_always_zero(PhaseGVN* phase, Node* shift, Node* mask, BasicType bt, bool check_reverse); - Node* AndIL_add_shift_and_mask(PhaseGVN* phase, BasicType bt); +protected: + Node* AndIL_sum_and_mask(PhaseGVN* phase, BasicType bt); }; //------------------------------MulINode--------------------------------------- diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestShiftAndMask.java b/test/hotspot/jtreg/compiler/c2/irTests/TestShiftAndMask.java index 4396873425ae1..d5766870118d7 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestShiftAndMask.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestShiftAndMask.java @@ -30,7 +30,7 @@ /* * @test - * @bug 8277850 8278949 8285793 + * @bug 8277850 8278949 8285793 8346664 * @summary C2: optimize mask checks in counted loops * @library /test/lib / * @run driver compiler.c2.irTests.TestShiftAndMask @@ -133,6 +133,30 @@ public static void addShiftMaskInt_runner() { } } + @Test + @IR(counts = { IRNode.AND_I, "1" }) + @IR(failOn = { IRNode.ADD_I, IRNode.LSHIFT_I }) + public static int addShiftPlusConstMaskInt(int i, int j) { + return (j + ((i + 5) << 2)) & 3; // transformed to: return j & 3; + } + + @Run(test = "addShiftPlusConstMaskInt") + public static void addShiftPlusConstMaskInt_runner() { + int i = RANDOM.nextInt(); + int j = RANDOM.nextInt(); + int res = addShiftPlusConstMaskInt(i, j); + if (res != (j & 3)) { + throw new RuntimeException("incorrect result: " + res); + } + } + + @Test + @Arguments(values = {Argument.RANDOM_EACH, Argument.RANDOM_EACH}) + @IR(counts = { IRNode.ADD_I, "2", IRNode.LSHIFT_I, "1" }) + public static int addShiftPlusConstDisjointMaskInt(int i, int j) { + return (j + ((i + 5) << 2)) & 32; // NOT transformed even though (5<<2) & 32 == 0 + } + @Test @IR(counts = { IRNode.AND_I, "1" }) @IR(failOn = { IRNode.ADD_I, IRNode.LSHIFT_I }) @@ -178,6 +202,23 @@ public static void addShiftMaskLong_runner() { } } + @Test + @IR(counts = { IRNode.AND_L, "1" }) + @IR(failOn = { IRNode.ADD_L, IRNode.LSHIFT_L }) + public static long addShiftPlusConstMaskLong(long i, long j) { + return (j + ((i - 5) << 2)) & 3; // transformed to: return j & 3; + } + + @Run(test = "addShiftPlusConstMaskLong") + public static void addShiftPlusConstMaskLong_runner() { + long i = RANDOM.nextLong(); + long j = RANDOM.nextLong(); + long res = addShiftPlusConstMaskLong(i, j); + if (res != (j & 3)) { + throw new RuntimeException("incorrect result: " + res); + } + } + @Test @IR(counts = { IRNode.AND_L, "1" }) @IR(failOn = { IRNode.ADD_L, IRNode.LSHIFT_L })