diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 6290f6be85fee6..27df00a92e667d 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -1703,7 +1703,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree) { // Move the checked bound to the right side for simplicity relopFunc = ValueNumStore::SwapRelop(relopFunc); - AssertionDsc dsc = AssertionDsc::CreateCompareCheckedBound(relopFunc, op2VN, op1VN, 0); + AssertionDsc dsc = AssertionDsc::CreateCompareCheckedBound(this, relopFunc, op2VN, op1VN, 0); AssertionIndex idx = optAddAssertion(dsc); optCreateComplementaryAssertion(idx); return idx; @@ -1712,7 +1712,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree) // "X CheckedBnd" if (!isUnsignedRelop && vnStore->IsVNCheckedBound(op2VN)) { - AssertionDsc dsc = AssertionDsc::CreateCompareCheckedBound(relopFunc, op1VN, op2VN, 0); + AssertionDsc dsc = AssertionDsc::CreateCompareCheckedBound(this, relopFunc, op1VN, op2VN, 0); AssertionIndex idx = optAddAssertion(dsc); optCreateComplementaryAssertion(idx); return idx; @@ -1725,7 +1725,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree) { // Move the (CheckedBnd + CNS) part to the right side for simplicity relopFunc = ValueNumStore::SwapRelop(relopFunc); - AssertionDsc dsc = AssertionDsc::CreateCompareCheckedBound(relopFunc, op2VN, checkedBnd, checkedBndCns); + AssertionDsc dsc = AssertionDsc::CreateCompareCheckedBound(this, relopFunc, op2VN, checkedBnd, checkedBndCns); AssertionIndex idx = optAddAssertion(dsc); optCreateComplementaryAssertion(idx); return idx; @@ -1734,7 +1734,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree) // "X (CheckedBnd + CNS)" if (!isUnsignedRelop && vnStore->IsVNCheckedBoundAddConst(op2VN, &checkedBnd, &checkedBndCns)) { - AssertionDsc dsc = AssertionDsc::CreateCompareCheckedBound(relopFunc, op1VN, checkedBnd, checkedBndCns); + AssertionDsc dsc = AssertionDsc::CreateCompareCheckedBound(this, relopFunc, op1VN, checkedBnd, checkedBndCns); AssertionIndex idx = optAddAssertion(dsc); optCreateComplementaryAssertion(idx); return idx; @@ -1748,7 +1748,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree) ValueNum idxVN = vnStore->VNNormalValue(unsignedCompareBnd.vnIdx); ValueNum lenVN = vnStore->VNNormalValue(unsignedCompareBnd.vnBound); - AssertionDsc dsc = AssertionDsc::CreateNoThrowArrBnd(idxVN, lenVN); + AssertionDsc dsc = AssertionDsc::CreateNoThrowArrBnd(this, idxVN, lenVN); AssertionIndex index = optAddAssertion(dsc); if (unsignedCompareBnd.cmpOper == VNF_GE_UN) { @@ -2006,6 +2006,21 @@ void Compiler::optAssertionGen(GenTree* tree) } break; + case GT_LCL_VAR: + if (!optLocalAssertionProp && tree->TypeIs(TYP_INT) && + lvaGetDesc(tree->AsLclVarCommon())->IsNeverNegative()) + { + // Create "LCL_VAR >= 0" assertion for int local variables that are never negative. + // Typically, it's Span.Length. + ValueNum opVN = optConservativeNormalVN(tree); + if (opVN != ValueNumStore::NoVN) + { + assertionInfo = optAddAssertion( + AssertionDsc::CreateConstantBound(this, VNF_GE, opVN, vnStore->VNZeroForType(TYP_INT))); + } + } + break; + case GT_IND: case GT_XAND: case GT_XORR: @@ -2036,8 +2051,9 @@ void Compiler::optAssertionGen(GenTree* tree) case GT_BOUNDS_CHECK: if (!optLocalAssertionProp) { - ValueNum idxVN = optConservativeNormalVN(tree->AsBoundsChk()->GetIndex()); - ValueNum lenVN = optConservativeNormalVN(tree->AsBoundsChk()->GetArrayLength()); + GenTree* arrLenNode = tree->AsBoundsChk()->GetArrayLength(); + ValueNum idxVN = optConservativeNormalVN(tree->AsBoundsChk()->GetIndex()); + ValueNum lenVN = optConservativeNormalVN(arrLenNode); if ((idxVN == ValueNumStore::NoVN) || (lenVN == ValueNumStore::NoVN)) { assertionInfo = NO_ASSERTION_INDEX; @@ -2047,7 +2063,7 @@ void Compiler::optAssertionGen(GenTree* tree) // GT_BOUNDS_CHECK node provides the following contract: // * idxVN < lenVN // * lenVN is non-negative - assertionInfo = optAddAssertion(AssertionDsc::CreateNoThrowArrBnd(idxVN, lenVN)); + assertionInfo = optAddAssertion(AssertionDsc::CreateNoThrowArrBnd(this, idxVN, lenVN)); } } break; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index ec02cb7caedf12..e8052fa1722ce5 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7712,7 +7712,6 @@ class Compiler enum optOp2Kind : uint8_t { - O2K_INVALID, O2K_LCLVAR_COPY, O2K_CONST_INT, O2K_CONST_DOUBLE, @@ -7732,6 +7731,7 @@ class Compiler friend struct AssertionDsc; // For AssertionDsc::Create* factory methods private: + INDEBUG(const Compiler* m_compiler); optOp1Kind m_kind; union { @@ -7753,6 +7753,7 @@ class Compiler ValueNum GetVN() const { + assert(!m_compiler->optLocalAssertionProp); // TODO-Cleanup: O1K_LCLVAR should be Local-AP only. assert(m_vn != ValueNumStore::NoVN); return m_vn; @@ -7760,6 +7761,7 @@ class Compiler unsigned GetLclNum() const { + assert(m_compiler->optLocalAssertionProp); assert(m_lclNum != BAD_VAR_NUM); assert(KindIs(O1K_LCLVAR)); return m_lclNum; @@ -7776,6 +7778,7 @@ class Compiler friend struct AssertionDsc; // For AssertionDsc::Create* factory methods private: + INDEBUG(const Compiler* m_compiler); optOp2Kind m_kind; bool m_checkedBoundIsNeverNegative; // only meaningful for O2K_CHECKED_BOUND_ADD_CNS kind uint16_t m_encodedIconFlags; // encoded icon gtFlags @@ -7806,6 +7809,7 @@ class Compiler unsigned GetLclNum() const { + assert(m_compiler->optLocalAssertionProp); assert(KindIs(O2K_LCLVAR_COPY)); return m_lclNum; } @@ -7824,6 +7828,7 @@ class Compiler IntegralRange GetIntegralRange() const { + assert(m_compiler->optLocalAssertionProp); assert(KindIs(O2K_SUBRANGE)); return m_range; } @@ -7837,6 +7842,7 @@ class Compiler ValueNum GetVN() const { + assert(!m_compiler->optLocalAssertionProp); assert(KindIs(O2K_CONST_INT, O2K_CONST_DOUBLE, O2K_ZEROOBJ)); assert(m_vn != ValueNumStore::NoVN); return m_vn; @@ -7845,6 +7851,7 @@ class Compiler // For "checkedBndVN + cns" form, return the "cns" part. int GetCheckedBoundConstant() const { + assert(!m_compiler->optLocalAssertionProp); assert(KindIs(O2K_CHECKED_BOUND_ADD_CNS)); assert(FitsIn(m_icon.m_iconVal)); return (int)m_icon.m_iconVal; @@ -7854,6 +7861,7 @@ class Compiler // We intentionally don't allow to use it via GetVN() to avoid confusion. ValueNum GetCheckedBound() const { + assert(!m_compiler->optLocalAssertionProp); assert(KindIs(O2K_CHECKED_BOUND_ADD_CNS)); assert(m_vn != ValueNumStore::NoVN); return m_vn; @@ -7861,6 +7869,7 @@ class Compiler bool IsCheckedBoundNeverNegative() const { + assert(!m_compiler->optLocalAssertionProp); assert(KindIs(O2K_CHECKED_BOUND_ADD_CNS)); return m_checkedBoundIsNeverNegative; } @@ -7911,6 +7920,14 @@ class Compiler optAssertionKind m_assertionKind; AssertionDscOp1 m_op1; AssertionDscOp2 m_op2; + + static AssertionDsc CreateEmptyAssertion(const Compiler* comp) + { + AssertionDsc dsc = {}; + INDEBUG(dsc.m_op1.m_compiler = comp); + INDEBUG(dsc.m_op2.m_compiler = comp); + return dsc; + } public: optAssertionKind GetKind() const @@ -8168,7 +8185,6 @@ class Compiler case O2K_SUBRANGE: return GetOp2().GetIntegralRange().Equals(that.GetOp2().GetIntegralRange()); - case O2K_INVALID: default: assert(!"Unexpected value for GetOp2().m_kind in AssertionDsc."); break; @@ -8203,7 +8219,7 @@ class Compiler GenTreeFlags iconFlags = GTF_EMPTY, FieldSeq* fldSeq = nullptr) { - AssertionDsc dsc = {}; + AssertionDsc dsc = CreateEmptyAssertion(comp); dsc.m_assertionKind = equals ? OAK_EQUAL : OAK_NOT_EQUAL; dsc.m_op1.m_kind = O1K_LCLVAR; @@ -8284,7 +8300,7 @@ class Compiler assert(lclNum1 != BAD_VAR_NUM); assert(lclNum2 != BAD_VAR_NUM); - AssertionDsc dsc = {}; + AssertionDsc dsc = CreateEmptyAssertion(comp); dsc.m_assertionKind = equals ? OAK_EQUAL : OAK_NOT_EQUAL; dsc.m_op1.m_kind = O1K_LCLVAR; dsc.m_op1.m_lclNum = lclNum1; @@ -8300,7 +8316,7 @@ class Compiler assert(comp->optLocalAssertionProp); assert(lclNum != BAD_VAR_NUM); - AssertionDsc dsc = {}; + AssertionDsc dsc = CreateEmptyAssertion(comp); dsc.m_assertionKind = OAK_SUBRANGE; dsc.m_op1.m_kind = O1K_LCLVAR; dsc.m_op1.m_lclNum = lclNum; @@ -8321,7 +8337,7 @@ class Compiler assert(!comp->vnStore->IsVNHandle(op2VN)); assert(!comp->optLocalAssertionProp); - AssertionDsc dsc = {}; + AssertionDsc dsc = CreateEmptyAssertion(comp); dsc.m_assertionKind = equals ? OAK_EQUAL : OAK_NOT_EQUAL; dsc.m_op1.m_vn = op1VN; dsc.m_op2.m_vn = op2VN; @@ -8336,7 +8352,7 @@ class Compiler { assert((objVN != ValueNumStore::NoVN) && comp->vnStore->IsVNTypeHandle(typeHndVN)); - AssertionDsc dsc = {}; + AssertionDsc dsc = CreateEmptyAssertion(comp); dsc.m_op1.m_kind = exact ? O1K_EXACT_TYPE : O1K_SUBTYPE; dsc.m_op1.m_vn = objVN; dsc.m_op2.m_kind = O2K_CONST_INT; @@ -8348,12 +8364,12 @@ class Compiler // Create a no-throw bounds check assertion: idxVN u< lenVN where lenVN is never negative // Effectively, this means "idxVN is in range [0, lenVN)". - static AssertionDsc CreateNoThrowArrBnd(ValueNum idxVN, ValueNum lenVN) + static AssertionDsc CreateNoThrowArrBnd(const Compiler* comp, ValueNum idxVN, ValueNum lenVN) { assert(idxVN != ValueNumStore::NoVN); assert(lenVN != ValueNumStore::NoVN); - AssertionDsc dsc = {}; + AssertionDsc dsc = CreateEmptyAssertion(comp); dsc.m_assertionKind = OAK_LT_UN; dsc.m_op1.m_kind = O1K_VN; dsc.m_op1.m_vn = idxVN; @@ -8367,12 +8383,13 @@ class Compiler } // Create "i (bnd + cns)" assertion - static AssertionDsc CreateCompareCheckedBound(VNFunc relop, ValueNum op1VN, ValueNum checkedBndVN, int cns) + static AssertionDsc CreateCompareCheckedBound( + const Compiler* comp, VNFunc relop, ValueNum op1VN, ValueNum checkedBndVN, int cns) { assert(op1VN != ValueNumStore::NoVN); assert(checkedBndVN != ValueNumStore::NoVN); - AssertionDsc dsc = {}; + AssertionDsc dsc = CreateEmptyAssertion(comp); dsc.m_assertionKind = FromVNFunc(relop); dsc.m_op1.m_kind = O1K_VN; dsc.m_op1.m_vn = op1VN; @@ -8391,7 +8408,7 @@ class Compiler bool op2IsCns = comp->vnStore->IsVNIntegralConstant(cnsVN, &cns); assert(op2IsCns); - AssertionDsc dsc = {}; + AssertionDsc dsc = CreateEmptyAssertion(comp); dsc.m_assertionKind = FromVNFunc(relop); dsc.m_op1.m_kind = O1K_VN; dsc.m_op1.m_vn = op1VN; diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 798afae835dbf9..475f2cb91605d3 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -1092,14 +1092,7 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp, int cnstLimit = (int)curAssertion.GetOp2().GetIntConstant(); assert(cnstLimit == comp->vnStore->CoercedConstantValue(curAssertion.GetOp2().GetVN())); - if ((cnstLimit == 0) && curAssertion.KindIs(Compiler::OAK_NOT_EQUAL) && canUseCheckedBounds && - comp->vnStore->IsVNCheckedBound(curAssertion.GetOp1().GetVN())) - { - // we have arr.Len != 0, so the length must be atleast one - limit = Limit(Limit::keConstant, 1); - cmpOper = GT_GE; - } - else if (curAssertion.KindIs(Compiler::OAK_EQUAL)) + if (curAssertion.KindIs(Compiler::OAK_EQUAL)) { limit = Limit(Limit::keConstant, cnstLimit); cmpOper = GT_EQ;