Skip to content

Commit 6066de1

Browse files
authored
Convert DIV/MOD to UDIV/UMOD using assertions (#94347)
1 parent 6d76479 commit 6066de1

File tree

5 files changed

+83
-25
lines changed

5 files changed

+83
-25
lines changed

src/coreclr/jit/assertionprop.cpp

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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);

src/coreclr/jit/compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7746,6 +7746,7 @@ class Compiler
77467746
GenTree* optAssertionProp_LclFld(ASSERT_VALARG_TP assertions, GenTreeLclVarCommon* tree, Statement* stmt);
77477747
GenTree* optAssertionProp_LocalStore(ASSERT_VALARG_TP assertions, GenTreeLclVarCommon* store, Statement* stmt);
77487748
GenTree* optAssertionProp_BlockStore(ASSERT_VALARG_TP assertions, GenTreeBlk* store, Statement* stmt);
7749+
GenTree* optAssertionProp_ModDiv(ASSERT_VALARG_TP assertions, GenTreeOp* tree, Statement* stmt);
77497750
GenTree* optAssertionProp_Return(ASSERT_VALARG_TP assertions, GenTreeUnOp* ret, Statement* stmt);
77507751
GenTree* optAssertionProp_Ind(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt);
77517752
GenTree* optAssertionProp_Cast(ASSERT_VALARG_TP assertions, GenTreeCast* cast, Statement* stmt);

src/coreclr/jit/gentree.cpp

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8764,8 +8764,6 @@ void GenTreeOp::CheckDivideByConstOptimized(Compiler* comp)
87648764
{
87658765
if (UsesDivideByConstOptimized(comp))
87668766
{
8767-
gtFlags |= GTF_DIV_BY_CNS_OPT;
8768-
87698767
// Now set DONT_CSE on the GT_CNS_INT divisor, note that
87708768
// with ValueNumbering we can have a non GT_CNS_INT divisor
87718769
GenTree* divisor = gtGetOp2()->gtEffectiveVal(/*commaOnly*/ true);
@@ -11143,18 +11141,6 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, _In_ _In_opt_
1114311141
}
1114411142
goto DASH;
1114511143

11146-
case GT_DIV:
11147-
case GT_MOD:
11148-
case GT_UDIV:
11149-
case GT_UMOD:
11150-
if (tree->gtFlags & GTF_DIV_BY_CNS_OPT)
11151-
{
11152-
printf("M"); // We will use a Multiply by reciprical
11153-
--msgLength;
11154-
break;
11155-
}
11156-
goto DASH;
11157-
1115811144
case GT_LCL_FLD:
1115911145
case GT_LCL_VAR:
1116011146
case GT_LCL_ADDR:

src/coreclr/jit/gentree.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -547,8 +547,6 @@ enum GenTreeFlags : unsigned int
547547

548548
GTF_DIV_MOD_NO_OVERFLOW = 0x40000000, // GT_DIV, GT_MOD -- Div or mod definitely does not overflow.
549549

550-
GTF_DIV_BY_CNS_OPT = 0x80000000, // GT_DIV -- Uses the division by constant optimization to compute this division
551-
552550
GTF_CHK_INDEX_INBND = 0x80000000, // GT_BOUNDS_CHECK -- have proven this check is always in-bounds
553551

554552
GTF_ARRLEN_NONFAULTING = 0x20000000, // GT_ARR_LENGTH -- An array length operation that cannot fault. Same as GT_IND_NONFAULTING.
@@ -3007,12 +3005,6 @@ struct GenTreeOp : public GenTreeUnOp
30073005
// then sets the flag GTF_DIV_BY_CNS_OPT and GTF_DONT_CSE on the constant
30083006
void CheckDivideByConstOptimized(Compiler* comp);
30093007

3010-
// True if this node is marked as using the division by constant optimization
3011-
bool MarkedDivideByConstOptimized() const
3012-
{
3013-
return (gtFlags & GTF_DIV_BY_CNS_OPT) != 0;
3014-
}
3015-
30163008
#if !defined(TARGET_64BIT) || defined(TARGET_ARM64)
30173009
bool IsValidLongMul();
30183010
#endif

src/coreclr/jit/lower.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6584,7 +6584,6 @@ bool Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod)
65846584
unreached();
65856585
#endif
65866586
}
6587-
assert(divMod->MarkedDivideByConstOptimized());
65886587

65896588
const bool requiresDividendMultiuse = !isDiv;
65906589
const weight_t curBBWeight = m_block->getBBWeight(comp);

0 commit comments

Comments
 (0)