diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 90a32f37ad49ca..0b2554b8fb974d 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -3825,41 +3825,63 @@ GenTree* Compiler::optAssertionProp_BlockStore(ASSERT_VALARG_TP assertions, GenT } //------------------------------------------------------------------------ -// optAssertionProp_ModDiv: Convert DIV/MOD to UDIV/UMOD if both operands -// can be proven to be non-negative, e.g.: -// -// if (x > 0) // creates "x > 0" assertion -// return x / 8; // DIV can be converted to UDIV +// optAssertionProp_RangeProperties: Obtains range properties for an arbitrary tree // // Arguments: -// assertions - set of live assertions -// tree - the DIV/MOD node to optimize -// stmt - statement containing DIV/MOD -// -// Returns: -// Updated UDIV/UMOD node, or "nullptr" -// -GenTree* Compiler::optAssertionProp_ModDiv(ASSERT_VALARG_TP assertions, GenTreeOp* tree, Statement* stmt) +// assertions - set of live assertions +// tree - the integral tree to analyze +// isKnownNonZero - [OUT] set to true if the tree is known to be non-zero +// isKnownNonNegative - [OUT] set to true if the tree is known to be non-negative +// +void Compiler::optAssertionProp_RangeProperties(ASSERT_VALARG_TP assertions, + GenTree* tree, + bool* isKnownNonZero, + bool* isKnownNonNegative) { - if (optLocalAssertionProp || !varTypeIsIntegral(tree) || BitVecOps::IsEmpty(apTraits, assertions)) + *isKnownNonZero = false; + *isKnownNonNegative = false; + + if (optLocalAssertionProp || !varTypeIsIntegral(tree) || BitVecOps::MayBeUninit(assertions) || + BitVecOps::IsEmpty(apTraits, assertions)) { - return nullptr; + return; } - // For now, we're mainly interested in "X op CNS" pattern (where CNS > 0). - // Technically, we can check assertions for both operands, but it's not clear if it's worth it. - if (!tree->gtGetOp2()->IsNeverNegative(this)) + // First, check simple properties without assertions. + *isKnownNonNegative = tree->IsNeverNegative(this); + *isKnownNonZero = tree->IsNeverZero(); + + if (*isKnownNonZero && *isKnownNonNegative) { - return nullptr; + // TP: We already have both properties, no need to check assertions. + return; } - const ValueNum dividendVN = vnStore->VNConservativeNormalValue(tree->gtGetOp1()->gtVNPair); + const ValueNum treeVN = vnStore->VNConservativeNormalValue(tree->gtVNPair); BitVecOps::Iter iter(apTraits, assertions); unsigned index = 0; while (iter.NextElem(&index)) { AssertionDsc* curAssertion = optGetAssertion(GetAssertionIndex(index)); + // First, analyze possible X ==/!= CNS assertions. + if (curAssertion->IsConstantInt32Assertion() && (curAssertion->op1.vn == treeVN)) + { + if ((curAssertion->assertionKind == OAK_NOT_EQUAL) && (curAssertion->op2.u1.iconVal == 0)) + { + // X != 0 --> definitely non-zero + // We can't say anything about X's non-negativity + *isKnownNonZero = true; + } + else if (curAssertion->assertionKind != OAK_NOT_EQUAL) + { + // X == CNS --> definitely non-negative if CNS >= 0 + // and definitely non-zero if CNS != 0 + *isKnownNonNegative = curAssertion->op2.u1.iconVal >= 0; + *isKnownNonZero = curAssertion->op2.u1.iconVal != 0; + } + } + // OAK_[NOT]_EQUAL assertion with op1 being O1K_CONSTANT_LOOP_BND // representing "(X relop CNS) ==/!= 0" assertion. if (!curAssertion->IsConstantBound() && !curAssertion->IsConstantBoundUnsigned()) @@ -3870,7 +3892,7 @@ GenTree* Compiler::optAssertionProp_ModDiv(ASSERT_VALARG_TP assertions, GenTreeO ValueNumStore::ConstantBoundInfo info; vnStore->GetConstantBoundInfo(curAssertion->op1.vn, &info); - if (info.cmpOpVN != dividendVN) + if (info.cmpOpVN != treeVN) { continue; } @@ -3893,27 +3915,70 @@ GenTree* Compiler::optAssertionProp_ModDiv(ASSERT_VALARG_TP assertions, GenTreeO if ((info.constVal >= 0)) { - bool isNotNegative = false; if (info.isUnsigned && ((cmpOper == GT_LT) || (cmpOper == GT_LE))) { // (uint)X <= CNS means X is [0..CNS] - isNotNegative = true; + *isKnownNonNegative = true; } else if (!info.isUnsigned && ((cmpOper == GT_GE) || (cmpOper == GT_GT))) { // X >= CNS means X is [CNS..unknown] - isNotNegative = true; - } - - if (isNotNegative) - { - JITDUMP("Converting DIV/MOD to unsigned UDIV/UMOD since both operands are never negative...\n") - tree->SetOper(tree->OperIs(GT_DIV) ? GT_UDIV : GT_UMOD, GenTree::PRESERVE_VN); - return optAssertionProp_Update(tree, tree, stmt); + *isKnownNonNegative = true; + *isKnownNonZero = (cmpOper == GT_GT) || (info.constVal > 0); } } } - return nullptr; +} + +//------------------------------------------------------------------------ +// optAssertionProp_ModDiv: Optimizes DIV/UDIV/MOD/UMOD via assertions +// 1) Convert DIV/MOD to UDIV/UMOD if both operands are proven to be never negative +// 2) Marks DIV/UDIV/MOD/UMOD with GTF_DIV_MOD_NO_BY_ZERO if divisor is proven to be never zero +// 3) Marks DIV/UDIV/MOD/UMOD with GTF_DIV_MOD_NO_OVERFLOW if both operands are proven to be never negative +// +// Arguments: +// assertions - set of live assertions +// tree - the DIV/UDIV/MOD/UMOD node to optimize +// stmt - statement containing DIV/UDIV/MOD/UMOD +// +// Returns: +// Updated DIV/UDIV/MOD/UMOD node, or nullptr +// +GenTree* Compiler::optAssertionProp_ModDiv(ASSERT_VALARG_TP assertions, GenTreeOp* tree, Statement* stmt) +{ + GenTree* op1 = tree->gtGetOp1(); + GenTree* op2 = tree->gtGetOp2(); + + bool op1IsNotZero; + bool op2IsNotZero; + bool op1IsNotNegative; + bool op2IsNotNegative; + optAssertionProp_RangeProperties(assertions, op1, &op1IsNotZero, &op1IsNotNegative); + optAssertionProp_RangeProperties(assertions, op2, &op2IsNotZero, &op2IsNotNegative); + + bool changed = false; + if (op1IsNotNegative && op2IsNotNegative && tree->OperIs(GT_DIV, GT_MOD)) + { + JITDUMP("Converting DIV/MOD to unsigned UDIV/UMOD since both operands are never negative...\n") + tree->SetOper(tree->OperIs(GT_DIV) ? GT_UDIV : GT_UMOD, GenTree::PRESERVE_VN); + changed = true; + } + + if (op2IsNotZero) + { + JITDUMP("Divisor for DIV/MOD is proven to be never negative...\n") + tree->gtFlags |= GTF_DIV_MOD_NO_BY_ZERO; + changed = true; + } + + if (op1IsNotNegative || op2IsNotNegative) + { + JITDUMP("DIV/MOD is proven to never overflow...\n") + tree->gtFlags |= GTF_DIV_MOD_NO_OVERFLOW; + changed = true; + } + + return changed ? optAssertionProp_Update(tree, tree, stmt) : nullptr; } //------------------------------------------------------------------------ @@ -4112,8 +4177,7 @@ GenTree* Compiler::optAssertionProp_RelOp(ASSERT_VALARG_TP assertions, GenTree* // // Currently only GT_EQ or GT_NE are supported Relops for local AssertionProp // - - if ((tree->gtOper != GT_EQ) && (tree->gtOper != GT_NE)) + if (!tree->OperIs(GT_EQ, GT_NE)) { return nullptr; } @@ -5261,6 +5325,8 @@ GenTree* Compiler::optAssertionProp(ASSERT_VALARG_TP assertions, GenTree* tree, case GT_MOD: case GT_DIV: + case GT_UMOD: + case GT_UDIV: return optAssertionProp_ModDiv(assertions, tree->AsOp(), stmt); case GT_BLK: diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 0a1319a8037475..9b581430e0306e 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7824,6 +7824,11 @@ class Compiler bool optNonNullAssertionProp_Ind(ASSERT_VALARG_TP assertions, GenTree* indir); bool optNonHeapAssertionProp_Ind(ASSERT_VALARG_TP assertions, GenTree* indir); + void optAssertionProp_RangeProperties(ASSERT_VALARG_TP assertions, + GenTree* tree, + bool* isKnownNonZero, + bool* isKnownNonNegative); + // Implied assertion functions. void optImpliedAssertions(AssertionIndex assertionIndex, ASSERT_TP& activeAssertions); void optImpliedByTypeOfAssertions(ASSERT_TP& activeAssertions); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index b0037c65dbe421..b1fb4bda7c0992 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -2707,6 +2707,14 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK) { return false; } + if (op1->OperIs(GT_MOD, GT_UMOD, GT_DIV, GT_UDIV)) + { + if ((op1->gtFlags & (GTF_DIV_MOD_NO_BY_ZERO | GTF_DIV_MOD_NO_OVERFLOW)) != + (op2->gtFlags & (GTF_DIV_MOD_NO_BY_ZERO | GTF_DIV_MOD_NO_OVERFLOW))) + { + return false; + } + } /* Figure out what kind of nodes we're comparing */ diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 1cec2935f7922b..163bcf3ac8ebe6 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -6492,7 +6492,7 @@ bool Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod) divisorValue -= 1; } - divMod->SetOper(newOper); + divMod->ChangeOper(newOper); divisor->AsIntCon()->SetIconValue(divisorValue); ContainCheckNode(divMod); return true; @@ -6504,7 +6504,7 @@ bool Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod) if (((type == TYP_INT) && (divisorValue > (UINT32_MAX / 2))) || ((type == TYP_LONG) && (divisorValue > (UINT64_MAX / 2)))) { - divMod->SetOper(GT_GE); + divMod->ChangeOper(GT_GE); divMod->gtFlags |= GTF_UNSIGNED; ContainCheckNode(divMod); return true; @@ -6639,7 +6639,7 @@ bool Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod) if (isDiv && !postShift && (type == TYP_I_IMPL)) { - divMod->SetOper(GT_MULHI); + divMod->ChangeOper(GT_MULHI); divMod->gtOp1 = adjustedDividend; divMod->SetUnsigned(); } @@ -6671,7 +6671,7 @@ bool Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod) if (isDiv && (type == TYP_I_IMPL)) { - divMod->SetOper(GT_RSZ); + divMod->ChangeOper(GT_RSZ); divMod->gtOp1 = mulhi; divMod->gtOp2 = shiftBy; } @@ -6689,7 +6689,7 @@ bool Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod) GenTree* mul = comp->gtNewOperNode(GT_MUL, type, mulhi, divisor); dividend = comp->gtNewLclvNode(dividend->AsLclVar()->GetLclNum(), dividend->TypeGet()); - divMod->SetOper(GT_SUB); + divMod->ChangeOper(GT_SUB); divMod->gtOp1 = dividend; divMod->gtOp2 = mul; @@ -6697,7 +6697,7 @@ bool Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod) } else if (type != TYP_I_IMPL) { - divMod->SetOper(GT_CAST); + divMod->ChangeOper(GT_CAST); divMod->AsCast()->gtCastType = TYP_INT; divMod->gtOp1 = mulhi; divMod->gtOp2 = nullptr; @@ -6787,7 +6787,7 @@ bool Lowering::TryLowerConstIntDivOrMod(GenTree* node, GenTree** nextNode) { // If the divisor is the minimum representable integer value then we can use a compare, // the result is 1 iff the dividend equals divisor. - divMod->SetOper(GT_EQ); + divMod->ChangeOper(GT_EQ); *nextNode = node; return true; } @@ -6882,7 +6882,7 @@ bool Lowering::TryLowerConstIntDivOrMod(GenTree* node, GenTree** nextNode) if (isDiv) { - divMod->SetOperRaw(GT_ADD); + divMod->ChangeOper(GT_ADD); divMod->AsOp()->gtOp1 = adjusted; divMod->AsOp()->gtOp2 = signBit; } @@ -6897,7 +6897,7 @@ bool Lowering::TryLowerConstIntDivOrMod(GenTree* node, GenTree** nextNode) GenTree* mul = comp->gtNewOperNode(GT_MUL, type, div, divisor); BlockRange().InsertBefore(divMod, dividend, div, divisor, mul); - divMod->SetOperRaw(GT_SUB); + divMod->ChangeOper(GT_SUB); divMod->AsOp()->gtOp1 = dividend; divMod->AsOp()->gtOp2 = mul; }