diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 348764908e83ed..69d06a5f3fb079 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -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(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. // @@ -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: @@ -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); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index dfafe03461d349..4427b86dcdd2ec 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -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); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 48d76cf396edb9..180149a5cf83a2 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -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); @@ -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: diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 005cb2bfe187f0..ff45d2def41686 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -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. @@ -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 diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 8a810c65d5cdcf..57ec7d2d4b2f2f 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -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);