@@ -3541,6 +3541,84 @@ GenTree* Compiler::optAssertionProp_BlockStore(ASSERT_VALARG_TP assertions, GenT
35413541 return nullptr ;
35423542}
35433543
3544+ // ------------------------------------------------------------------------
3545+ // optAssertionProp_ModDiv: Convert DIV/MOD to UDIV/UMOD if both operands
3546+ // can be proven to be non-negative, e.g.:
3547+ //
3548+ // if (x > 0) // creates "x > 0" assertion
3549+ // return x / 8; // DIV can be converted to UDIV
3550+ //
3551+ // Arguments:
3552+ // assertions - set of live assertions
3553+ // tree - the DIV/MOD node to optimize
3554+ // stmt - statement containing DIV/MOD
3555+ //
3556+ // Returns:
3557+ // Updated UDIV/UMOD node, or "nullptr"
3558+ //
3559+ GenTree* Compiler::optAssertionProp_ModDiv (ASSERT_VALARG_TP assertions, GenTreeOp* tree, Statement* stmt)
3560+ {
3561+ if (optLocalAssertionProp || !varTypeIsIntegral (tree) || BitVecOps::IsEmpty (apTraits, assertions))
3562+ {
3563+ return nullptr ;
3564+ }
3565+
3566+ // For now, we're mainly interested in "X op CNS" pattern (where CNS > 0).
3567+ // Technically, we can check assertions for both operands, but it's not clear if it's worth it.
3568+ if (!tree->gtGetOp2 ()->IsNeverNegative (this ))
3569+ {
3570+ return nullptr ;
3571+ }
3572+
3573+ const ValueNum dividendVN = vnStore->VNConservativeNormalValue (tree->gtGetOp1 ()->gtVNPair );
3574+ BitVecOps::Iter iter (apTraits, assertions);
3575+ unsigned index = 0 ;
3576+ while (iter.NextElem (&index))
3577+ {
3578+ AssertionDsc* curAssertion = optGetAssertion (GetAssertionIndex (index));
3579+
3580+ // OAK_[NOT]_EQUAL assertion with op1 being O1K_CONSTANT_LOOP_BND
3581+ // representing "(X relop CNS) ==/!= 0" assertion.
3582+ if (!curAssertion->IsConstantBound ())
3583+ {
3584+ continue ;
3585+ }
3586+
3587+ ValueNumStore::ConstantBoundInfo info;
3588+ vnStore->GetConstantBoundInfo (curAssertion->op1 .vn , &info);
3589+
3590+ if (info.cmpOpVN != dividendVN)
3591+ {
3592+ continue ;
3593+ }
3594+
3595+ // Root assertion has to be either:
3596+ // (X relop CNS) == 0
3597+ // (X relop CNS) != 0
3598+ if ((curAssertion->op2 .kind != O2K_CONST_INT) || (curAssertion->op2 .u1 .iconVal != 0 ))
3599+ {
3600+ continue ;
3601+ }
3602+
3603+ genTreeOps cmpOper = static_cast <genTreeOps>(info.cmpOper );
3604+
3605+ // Normalize "(X relop CNS) == false" to "(X reversed_relop CNS) == true"
3606+ if (curAssertion->assertionKind == OAK_EQUAL)
3607+ {
3608+ cmpOper = GenTree::ReverseRelop (cmpOper);
3609+ }
3610+
3611+ // "X >= CNS" or "X > CNS" where CNS is >= 0
3612+ if ((info.constVal >= 0 ) && ((cmpOper == GT_GE) || (cmpOper == GT_GT)))
3613+ {
3614+ JITDUMP (" Converting DIV/MOD to unsigned UDIV/UMOD since both operands are never negative...\n " )
3615+ tree->SetOper (tree->OperIs (GT_DIV) ? GT_UDIV : GT_UMOD, GenTree::PRESERVE_VN);
3616+ return optAssertionProp_Update (tree, tree, stmt);
3617+ }
3618+ }
3619+ return nullptr ;
3620+ }
3621+
35443622// ------------------------------------------------------------------------
35453623// optAssertionProp_Return: Try and optimize a GT_RETURN via assertions.
35463624//
@@ -4787,6 +4865,10 @@ GenTree* Compiler::optAssertionProp(ASSERT_VALARG_TP assertions, GenTree* tree,
47874865 case GT_RETURN:
47884866 return optAssertionProp_Return (assertions, tree->AsUnOp (), stmt);
47894867
4868+ case GT_MOD:
4869+ case GT_DIV:
4870+ return optAssertionProp_ModDiv (assertions, tree->AsOp (), stmt);
4871+
47904872 case GT_BLK:
47914873 case GT_IND:
47924874 case GT_STOREIND:
@@ -4812,11 +4894,9 @@ GenTree* Compiler::optAssertionProp(ASSERT_VALARG_TP assertions, GenTree* tree,
48124894 case GT_LE:
48134895 case GT_GT:
48144896 case GT_GE:
4815-
48164897 return optAssertionProp_RelOp (assertions, tree, stmt);
48174898
48184899 case GT_JTRUE:
4819-
48204900 if (block != nullptr )
48214901 {
48224902 return optVNConstantPropOnJTrue (block, tree);
0 commit comments