Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert DIV/MOD to UDIV/UMOD using assertions #94347

Merged
merged 9 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 82 additions & 2 deletions src/coreclr/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3551,6 +3551,84 @@ GenTree* Compiler::optAssertionProp_BlockStore(ASSERT_VALARG_TP assertions, GenT
return nullptr;
}

//------------------------------------------------------------------------
// 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
//
// 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)
{
if (optLocalAssertionProp || !varTypeIsIntegral(tree) || BitVecOps::IsEmpty(apTraits, assertions))
{
return nullptr;
}

// 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))
{
return nullptr;
}

const ValueNum dividendVN = vnStore->VNConservativeNormalValue(tree->gtGetOp1()->gtVNPair);
BitVecOps::Iter iter(apTraits, assertions);
unsigned index = 0;
while (iter.NextElem(&index))
{
AssertionDsc* curAssertion = optGetAssertion(GetAssertionIndex(index));

// OAK_[NOT]_EQUAL assertion with op1 being O1K_CONSTANT_LOOP_BND
// representing "(X relop CNS) ==/!= 0" assertion.
if (!curAssertion->IsConstantBound())
{
continue;
}

ValueNumStore::ConstantBoundInfo info;
vnStore->GetConstantBoundInfo(curAssertion->op1.vn, &info);

if (info.cmpOpVN != dividendVN)
{
continue;
}

// Root assertion has to be either:
// (X relop CNS) == 0
// (X relop CNS) != 0
if ((curAssertion->op2.kind != O2K_CONST_INT) || (curAssertion->op2.u1.iconVal != 0))
{
continue;
}

genTreeOps cmpOper = static_cast<genTreeOps>(info.cmpOper);

// Normalize "(X relop CNS) == false" to "(X reversed_relop CNS) == true"
if (curAssertion->assertionKind == OAK_EQUAL)
{
cmpOper = GenTree::ReverseRelop(cmpOper);
}

// "X >= CNS" or "X > CNS" where CNS is >= 0
if ((info.constVal >= 0) && ((cmpOper == GT_GE) || (cmpOper == GT_GT)))
{
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);
}
}
return nullptr;
}

//------------------------------------------------------------------------
// optAssertionProp_Return: Try and optimize a GT_RETURN via assertions.
//
Expand Down Expand Up @@ -4787,6 +4865,10 @@ GenTree* Compiler::optAssertionProp(ASSERT_VALARG_TP assertions, GenTree* tree,
case GT_RETURN:
return optAssertionProp_Return(assertions, tree->AsUnOp(), stmt);

case GT_MOD:
case GT_DIV:
return optAssertionProp_ModDiv(assertions, tree->AsOp(), stmt);

case GT_BLK:
case GT_IND:
case GT_STOREIND:
Expand All @@ -4812,11 +4894,9 @@ GenTree* Compiler::optAssertionProp(ASSERT_VALARG_TP assertions, GenTree* tree,
case GT_LE:
case GT_GT:
case GT_GE:

return optAssertionProp_RelOp(assertions, tree, stmt);

case GT_JTRUE:

if (block != nullptr)
{
return optVNConstantPropOnJTrue(block, tree);
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -7742,6 +7742,7 @@ class Compiler
GenTree* optAssertionProp_LclFld(ASSERT_VALARG_TP assertions, GenTreeLclVarCommon* tree, Statement* stmt);
GenTree* optAssertionProp_LocalStore(ASSERT_VALARG_TP assertions, GenTreeLclVarCommon* store, Statement* stmt);
GenTree* optAssertionProp_BlockStore(ASSERT_VALARG_TP assertions, GenTreeBlk* store, Statement* stmt);
GenTree* optAssertionProp_ModDiv(ASSERT_VALARG_TP assertions, GenTreeOp* tree, Statement* stmt);
GenTree* optAssertionProp_Return(ASSERT_VALARG_TP assertions, GenTreeUnOp* ret, Statement* stmt);
GenTree* optAssertionProp_Ind(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt);
GenTree* optAssertionProp_Cast(ASSERT_VALARG_TP assertions, GenTreeCast* cast, Statement* stmt);
Expand Down
14 changes: 0 additions & 14 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8764,8 +8764,6 @@ void GenTreeOp::CheckDivideByConstOptimized(Compiler* comp)
{
if (UsesDivideByConstOptimized(comp))
{
gtFlags |= GTF_DIV_BY_CNS_OPT;

// Now set DONT_CSE on the GT_CNS_INT divisor, note that
// with ValueNumbering we can have a non GT_CNS_INT divisor
GenTree* divisor = gtGetOp2()->gtEffectiveVal(/*commaOnly*/ true);
Expand Down Expand Up @@ -11143,18 +11141,6 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, _In_ _In_opt_
}
goto DASH;

case GT_DIV:
case GT_MOD:
case GT_UDIV:
case GT_UMOD:
if (tree->gtFlags & GTF_DIV_BY_CNS_OPT)
{
printf("M"); // We will use a Multiply by reciprical
--msgLength;
break;
}
goto DASH;

case GT_LCL_FLD:
case GT_LCL_VAR:
case GT_LCL_ADDR:
Expand Down
8 changes: 0 additions & 8 deletions src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -547,8 +547,6 @@ enum GenTreeFlags : unsigned int

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

GTF_DIV_BY_CNS_OPT = 0x80000000, // GT_DIV -- Uses the division by constant optimization to compute this division

GTF_CHK_INDEX_INBND = 0x80000000, // GT_BOUNDS_CHECK -- have proven this check is always in-bounds

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

// True if this node is marked as using the division by constant optimization
bool MarkedDivideByConstOptimized() const
{
return (gtFlags & GTF_DIV_BY_CNS_OPT) != 0;
}

#if !defined(TARGET_64BIT) || defined(TARGET_ARM64)
bool IsValidLongMul();
#endif
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/jit/lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6584,7 +6584,6 @@ bool Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod)
unreached();
#endif
}
assert(divMod->MarkedDivideByConstOptimized());

const bool requiresDividendMultiuse = !isDiv;
const weight_t curBBWeight = m_block->getBBWeight(comp);
Expand Down
Loading