@@ -59,13 +59,29 @@ int RangeCheck::GetArrLength(ValueNum vn)
5959 return m_pCompiler->vnStore ->GetNewArrSize (arrRefVN);
6060}
6161
62- // Check if the computed range is within bounds.
63- bool RangeCheck::BetweenBounds (Range& range, int lower, GenTree* upper)
62+ // ------------------------------------------------------------------------
63+ // BetweenBounds: Check if the computed range is within bounds
64+ //
65+ // Arguments:
66+ // Range - the range to check if in bounds
67+ // upper - the array length vn
68+ // arrSize - the length of the array if known, or <= 0
69+ //
70+ // Return Value:
71+ // True iff range is between [0 and vn - 1] or [0, arrSize - 1]
72+ //
73+ // notes:
74+ // This function assumes that the lower range is resolved and upper range is symbolic as in an
75+ // increasing loop.
76+ //
77+ // TODO-CQ: This is not general enough.
78+ //
79+ bool RangeCheck::BetweenBounds (Range& range, GenTree* upper, int arrSize)
6480{
6581#ifdef DEBUG
6682 if (m_pCompiler->verbose )
6783 {
68- printf (" %s BetweenBounds <%d, " , range.ToString (m_pCompiler->getAllocatorDebugOnly ()), lower );
84+ printf (" %s BetweenBounds <%d, " , range.ToString (m_pCompiler->getAllocatorDebugOnly ()), 0 );
6985 Compiler::printTreeID (upper);
7086 printf (" >\n " );
7187 }
@@ -85,28 +101,9 @@ bool RangeCheck::BetweenBounds(Range& range, int lower, GenTree* upper)
85101 JITDUMP (" \n " );
86102#endif
87103
88- int arrSize = 0 ;
89-
90- if (vnStore->IsVNConstant (uLimitVN))
104+ if ((arrSize <= 0 ) && !vnStore->IsVNCheckedBound (uLimitVN))
91105 {
92- ssize_t constVal = -1 ;
93- unsigned iconFlags = 0 ;
94-
95- if (m_pCompiler->optIsTreeKnownIntValue (true , upper, &constVal, &iconFlags))
96- {
97- arrSize = (int )constVal;
98- }
99- }
100- else if (vnStore->IsVNArrLen (uLimitVN))
101- {
102- // Get the array reference from the length.
103- ValueNum arrRefVN = vnStore->GetArrForLenVn (uLimitVN);
104- // Check if array size can be obtained.
105- arrSize = vnStore->GetNewArrSize (arrRefVN);
106- }
107- else if (!vnStore->IsVNCheckedBound (uLimitVN))
108- {
109- // If the upper limit is not length, then bail.
106+ // If we don't know the array size and the upper limit is not known, then bail.
110107 return false ;
111108 }
112109
@@ -231,6 +228,19 @@ void RangeCheck::OptimizeRangeCheck(BasicBlock* block, Statement* stmt, GenTree*
231228#endif // FEATURE_SIMD
232229 {
233230 arrSize = GetArrLength (arrLenVn);
231+
232+ // if we can't find the array length, see if there
233+ // are any assertions about the array size we can use to get a minimum length
234+ if (arrSize <= 0 )
235+ {
236+ JITDUMP (" Looking for array size assertions for: " FMT_VN " \n " , arrLenVn);
237+ Range arrLength = Range (Limit (Limit::keDependent));
238+ MergeEdgeAssertions (arrLenVn, block->bbAssertionIn , &arrLength);
239+ if (arrLength.lLimit .IsConstant ())
240+ {
241+ arrSize = arrLength.lLimit .GetConstant ();
242+ }
243+ }
234244 }
235245
236246 JITDUMP (" ArrSize for lengthVN:%03X = %d\n " , arrLenVn, arrSize);
@@ -286,7 +296,7 @@ void RangeCheck::OptimizeRangeCheck(BasicBlock* block, Statement* stmt, GenTree*
286296 }
287297
288298 // Is the range between the lower and upper bound values.
289- if (BetweenBounds (range, 0 , bndsChk->gtArrLen ))
299+ if (BetweenBounds (range, bndsChk->gtArrLen , arrSize ))
290300 {
291301 JITDUMP (" [RangeCheck::OptimizeRangeCheck] Between bounds\n " );
292302 m_pCompiler->optRemoveRangeCheck (treeParent, stmt);
@@ -532,18 +542,51 @@ void RangeCheck::SetDef(UINT64 hash, Location* loc)
532542}
533543#endif
534544
535- // Merge assertions on the edge flowing into the block about a variable.
545+ // ------------------------------------------------------------------------
546+ // MergeEdgeAssertions: Merge assertions on the edge flowing into the block about a variable
547+ //
548+ // Arguments:
549+ // GenTreeLclVarCommon - the variable to look for assertions for
550+ // assertions - the assertions to use
551+ // pRange - the range to tighten with assertions
552+ //
536553void RangeCheck::MergeEdgeAssertions (GenTreeLclVarCommon* lcl, ASSERT_VALARG_TP assertions, Range* pRange)
554+ {
555+ if (lcl->GetSsaNum () == SsaConfig::RESERVED_SSA_NUM)
556+ {
557+ return ;
558+ }
559+
560+ LclVarDsc* varDsc = m_pCompiler->lvaGetDesc (lcl);
561+ if (varDsc->CanBeReplacedWithItsField (m_pCompiler))
562+ {
563+ varDsc = m_pCompiler->lvaGetDesc (varDsc->lvFieldLclStart );
564+ }
565+ LclSsaVarDsc* ssaData = varDsc->GetPerSsaData (lcl->GetSsaNum ());
566+ ValueNum normalLclVN = m_pCompiler->vnStore ->VNConservativeNormalValue (ssaData->m_vnPair );
567+ MergeEdgeAssertions (normalLclVN, assertions, pRange);
568+ }
569+
570+ // ------------------------------------------------------------------------
571+ // MergeEdgeAssertions: Merge assertions on the edge flowing into the block about a variable
572+ //
573+ // Arguments:
574+ // normalLclVN - the value number to look for assertions for
575+ // assertions - the assertions to use
576+ // pRange - the range to tighten with assertions
577+ //
578+ void RangeCheck::MergeEdgeAssertions (ValueNum normalLclVN, ASSERT_VALARG_TP assertions, Range* pRange)
537579{
538580 if (BitVecOps::IsEmpty (m_pCompiler->apTraits , assertions))
539581 {
540582 return ;
541583 }
542584
543- if (lcl-> GetSsaNum () == SsaConfig::RESERVED_SSA_NUM )
585+ if (normalLclVN == ValueNumStore::NoVN )
544586 {
545587 return ;
546588 }
589+
547590 // Walk through the "assertions" to check if the apply.
548591 BitVecOps::Iter iter (m_pCompiler->apTraits , assertions);
549592 unsigned index = 0 ;
@@ -554,15 +597,8 @@ void RangeCheck::MergeEdgeAssertions(GenTreeLclVarCommon* lcl, ASSERT_VALARG_TP
554597 Compiler::AssertionDsc* curAssertion = m_pCompiler->optGetAssertion (assertionIndex);
555598
556599 Limit limit (Limit::keUndef);
557- genTreeOps cmpOper = GT_NONE;
558-
559- LclVarDsc* varDsc = m_pCompiler->lvaGetDesc (lcl);
560- if (varDsc->CanBeReplacedWithItsField (m_pCompiler))
561- {
562- varDsc = m_pCompiler->lvaGetDesc (varDsc->lvFieldLclStart );
563- }
564- LclSsaVarDsc* ssaData = varDsc->GetPerSsaData (lcl->GetSsaNum ());
565- ValueNum normalLclVN = m_pCompiler->vnStore ->VNConservativeNormalValue (ssaData->m_vnPair );
600+ genTreeOps cmpOper = GT_NONE;
601+ bool isConstantAssertion = false ;
566602
567603 // Current assertion is of the form (i < len - cns) != 0
568604 if (curAssertion->IsCheckedBoundArithBound ())
@@ -602,13 +638,20 @@ void RangeCheck::MergeEdgeAssertions(GenTreeLclVarCommon* lcl, ASSERT_VALARG_TP
602638 m_pCompiler->vnStore ->GetCompareCheckedBound (curAssertion->op1 .vn , &info);
603639
604640 // If we don't have the same variable we are comparing against, bail.
605- if (normalLclVN != info.cmpOp )
641+ if (normalLclVN == info.cmpOp )
642+ {
643+ cmpOper = (genTreeOps)info.cmpOper ;
644+ limit = Limit (Limit::keBinOpArray, info.vnBound , 0 );
645+ }
646+ else if (normalLclVN == info.vnBound )
647+ {
648+ cmpOper = GenTree::SwapRelop ((genTreeOps)info.cmpOper );
649+ limit = Limit (Limit::keBinOpArray, info.cmpOp , 0 );
650+ }
651+ else
606652 {
607653 continue ;
608654 }
609-
610- limit = Limit (Limit::keBinOpArray, info.vnBound , 0 );
611- cmpOper = (genTreeOps)info.cmpOper ;
612655 }
613656 // Current assertion is of the form (i < 100) != 0
614657 else if (curAssertion->IsConstantBound ())
@@ -627,6 +670,36 @@ void RangeCheck::MergeEdgeAssertions(GenTreeLclVarCommon* lcl, ASSERT_VALARG_TP
627670 limit = Limit (Limit::keConstant, info.constVal );
628671 cmpOper = (genTreeOps)info.cmpOper ;
629672 }
673+ // Current assertion is of the form i == 100
674+ else if (curAssertion->IsConstantInt32Assertion ())
675+ {
676+ if (curAssertion->op1 .vn != normalLclVN)
677+ {
678+ continue ;
679+ }
680+
681+ int cnstLimit = m_pCompiler->vnStore ->CoercedConstantValue <int >(curAssertion->op2 .vn );
682+
683+ if ((cnstLimit == 0 ) && (curAssertion->assertionKind == Compiler::OAK_NOT_EQUAL) &&
684+ m_pCompiler->vnStore ->IsVNCheckedBound (curAssertion->op1 .vn ))
685+ {
686+ // we have arr.Len != 0, so the length must be atleast one
687+ limit = Limit (Limit::keConstant, 1 );
688+ cmpOper = GT_GE;
689+ }
690+ else if (curAssertion->assertionKind == Compiler::OAK_EQUAL)
691+ {
692+ limit = Limit (Limit::keConstant, cnstLimit);
693+ cmpOper = GT_EQ;
694+ }
695+ else
696+ {
697+ // We have a != assertion, but it doesn't tell us much about the interval. So just skip it.
698+ continue ;
699+ }
700+
701+ isConstantAssertion = true ;
702+ }
630703 // Current assertion is not supported, ignore it
631704 else
632705 {
@@ -635,8 +708,8 @@ void RangeCheck::MergeEdgeAssertions(GenTreeLclVarCommon* lcl, ASSERT_VALARG_TP
635708
636709 assert (limit.IsBinOpArray () || limit.IsConstant ());
637710
638- // Make sure the assertion is of the form != 0 or == 0.
639- if (curAssertion->op2 .vn != m_pCompiler->vnStore ->VNZeroForType (TYP_INT))
711+ // Make sure the assertion is of the form != 0 or == 0 if it isn't a constant assertion .
712+ if (!isConstantAssertion && ( curAssertion->op2 .vn != m_pCompiler->vnStore ->VNZeroForType (TYP_INT) ))
640713 {
641714 continue ;
642715 }
@@ -647,6 +720,17 @@ void RangeCheck::MergeEdgeAssertions(GenTreeLclVarCommon* lcl, ASSERT_VALARG_TP
647720 }
648721#endif
649722
723+ // Limits are sometimes made with the form vn + constant, where vn is a known constant
724+ // see if we can simplify this to just a constant
725+ if (limit.IsBinOpArray () && m_pCompiler->vnStore ->IsVNInt32Constant (limit.vn ))
726+ {
727+ Limit tempLimit = Limit (Limit::keConstant, m_pCompiler->vnStore ->ConstantValue <int >(limit.vn ));
728+ if (tempLimit.AddConstant (limit.cns ))
729+ {
730+ limit = tempLimit;
731+ }
732+ }
733+
650734 ValueNum arrLenVN = m_pCompiler->vnStore ->VNConservativeNormalValue (m_pCurBndsChk->gtArrLen ->gtVNPair );
651735
652736 if (m_pCompiler->vnStore ->IsVNConstant (arrLenVN))
@@ -663,22 +747,25 @@ void RangeCheck::MergeEdgeAssertions(GenTreeLclVarCommon* lcl, ASSERT_VALARG_TP
663747 // (i < length) != 0
664748 // (i < 100) == 0
665749 // (i < 100) != 0
750+ // i == 100
666751 //
667- // At this point, we have detected that op1.vn is (i < length) or (i < length + cns) or
668- // (i < 100) and the op2.vn is 0.
752+ // At this point, we have detected that either op1.vn is (i < length) or (i < length + cns) or
753+ // (i < 100) and the op2.vn is 0 or that op1.vn is i and op2.vn is a known constant .
669754 //
670755 // Now, let us check if we are == 0 (i.e., op1 assertion is false) or != 0 (op1 assertion
671- // is true.),
756+ // is true.).
672757 //
673- // If we have an assertion of the form == 0 (i.e., equals false), then reverse relop.
758+ // If we have a non-constant assertion of the form == 0 (i.e., equals false), then reverse relop.
674759 // The relop has to be reversed because we have: (i < length) is false which is the same
675760 // as (i >= length).
676- if (curAssertion->assertionKind == Compiler::OAK_EQUAL)
761+ if (( curAssertion->assertionKind == Compiler::OAK_EQUAL) && !isConstantAssertion )
677762 {
678763 cmpOper = GenTree::ReverseRelop (cmpOper);
679764 }
680765
681- // Bounds are inclusive, so add -1 for upper bound when "<". But make sure we won't overflow.
766+ assert (cmpOper != GT_NONE);
767+
768+ // Bounds are inclusive, so add -1 for upper bound when "<". But make sure we won't underflow.
682769 if (cmpOper == GT_LT && !limit.AddConstant (-1 ))
683770 {
684771 continue ;
@@ -744,6 +831,11 @@ void RangeCheck::MergeEdgeAssertions(GenTreeLclVarCommon* lcl, ASSERT_VALARG_TP
744831 pRange->lLimit = limit;
745832 break ;
746833
834+ case GT_EQ:
835+ pRange->uLimit = limit;
836+ pRange->lLimit = limit;
837+ break ;
838+
747839 default :
748840 // All other 'cmpOper' kinds leave lLimit/uLimit unchanged
749841 break ;
0 commit comments