@@ -5502,13 +5502,17 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions,
55025502 ValueNum vnCurIdx = optConservativeNormalVN (arrBndsChkIdx);
55035503 ValueNum vnCurLen = optConservativeNormalVN (arrBndsChkLen);
55045504
5505+ // Let's see if we can remove the bounds check based on the ranges.
5506+ Range idxRng = GetRange (this , arrBndsChkIdx, block, assertions, /* fast*/ true );
5507+
55055508 auto dropBoundsCheck = [&](INDEBUG (const char * reason)) -> GenTree* {
55065509 JITDUMP (" \n Removing redundant (%s) bounds check in " FMT_BB " :\n " , reason, compCurBB->bbNum );
55075510 DISPTREE (tree);
55085511
55095512 // Extract the side effects of idx and len. We deliberately ignore the potential null-check
55105513 // exception of the length (e.g. GT_ARR_LENGTH): having proven the bounds check redundant, we
55115514 // have also proven the length load is non-faulting. This mirrors the hack in optRemoveRangeCheck.
5515+ // TODO-Bug: We really should be extracting all side effects from the length and index here.
55125516 GenTree* sideEffList = nullptr ;
55135517 gtExtractSideEffList (arrBndsChkLen, &sideEffList, GTF_ASG );
55145518 gtExtractSideEffList (arrBndsChkIdx, &sideEffList);
@@ -5534,63 +5538,26 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions,
55345538 // Do we have a previous range check involving the same 'vnLen' upper bound?
55355539 if (curAssertion.GetOp2 ().GetVN () == optConservativeNormalVN (arrBndsChkLen))
55365540 {
5537- // Do we have the exact same lower bound 'vnIdx'?
5538- // a[i] followed by a[i]
55395541 if (curAssertion.GetOp1 ().GetVN () == vnCurIdx)
55405542 {
55415543 return dropBoundsCheck (INDEBUG (" a[i] followed by a[i]" ));
55425544 }
5543- // Are we using zero as the index?
5544- // It can always be considered as redundant with any previous value
5545- // a[*] followed by a[0]
5546- else if (vnCurIdx == vnStore->VNZeroForType (arrBndsChkIdx->TypeGet ()))
5547- {
5548- return dropBoundsCheck (INDEBUG (" a[*] followed by a[0]" ));
5549- }
5550- else
5545+ else if (idxRng.IsConstantRange () && idxRng.LowerLimit ().GetConstant () >= 0 )
55515546 {
5552- // index1 doesn't have to be a constant, it can be a Phi each predecessor of which is a constant.
5553- // The smallest of those is what we can rely on. Example:
5554- //
5555- // arr[cond ? 10 : 5] = 0; // arr is at least 6 elements long
5556- // arr[2] = 0; // arr must be at least 3 elements long
5557- //
5558- // or even:
5547+ // Get the range of the previously checked index for the same array length.
5548+ Range rngOfPrevIdx = RangeCheck::GetRangeFromAssertions (this , curAssertion.GetOp1 ().GetVN (), nullptr );
5549+
5550+ // We know the range of the previous index, we know the range of the current index.
55595551 //
5560- // arr[cond ? 10 : 5 ] = 0; // arr is at least 6 elements long
5561- // arr[cond ? 1 : 2 ] = 0; // arr must be at least 3 elements long
5552+ // a[prevIdx ] = 0; // e.g. prevIdx's range is [5..10]
5553+ // a[currIdx ] = 0; // e.g. currIdx's range is [0..5] -> drop bounds check for currIdx
55625554 //
5563- auto tryGetMaxOrMinConst = [this ](ValueNum vn, bool getMin, int * index) -> bool {
5564- *index = getMin ? INT_MAX : INT_MIN ;
5565- return vnStore->VNVisitReachingVNs (vn,
5566- [this , index, getMin](ValueNum vn) -> ValueNumStore::VNVisit {
5567- int cns = 0 ;
5568- if (vnStore->IsVNIntegralConstant (vn, &cns))
5569- {
5570- *index = getMin ? min (*index, cns) : max (*index, cns);
5571- return ValueNumStore::VNVisit::Continue;
5572- }
5573- return ValueNumStore::VNVisit::Abort;
5574- }) == ValueNumStore::VNVisit::Continue;
5575- };
5576-
5577- int index1;
5578- int index2;
5579- if (tryGetMaxOrMinConst (curAssertion.GetOp1 ().GetVN (), /* min*/ true , &index1) &&
5580- tryGetMaxOrMinConst (vnCurIdx, /* max*/ false , &index2))
5555+ if (rngOfPrevIdx.IsConstantRange () &&
5556+ (rngOfPrevIdx.LowerLimit ().GetConstant () >= idxRng.UpperLimit ().GetConstant ()))
55815557 {
5582- // It can always be considered as redundant with any previous higher constant value
5583- // a[K1] followed by a[K2], with K2 >= 0 and K1 >= K2
5584- if (index2 >= 0 && index1 >= index2)
5585- {
5586- return dropBoundsCheck (INDEBUG (" a[K1] followed by a[K2], with K2 >= 0 and K1 >= K2" ));
5587- }
5558+ return dropBoundsCheck (INDEBUG (" current index is within the previous range" ));
55885559 }
55895560 }
5590- // Extend this to remove additional redundant bounds checks:
5591- // i.e. a[i+1] followed by a[i] by using the VN(i+1) >= VN(i)
5592- // a[i] followed by a[j] when j is known to be >= i
5593- // a[i] followed by a[5] when i is known to be >= 5
55945561 }
55955562 }
55965563
@@ -5629,8 +5596,6 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions,
56295596 return dropBoundsCheck (INDEBUG (" a[X u% a.Length] is always within bounds" ));
56305597 }
56315598
5632- // Let's see if we can remove the bounds check based on the ranges.
5633- Range idxRng = GetRange (this , arrBndsChkIdx, block, assertions, /* fast*/ true );
56345599 Range lenRng = GetRange (this , arrBndsChkLen, block, assertions, /* fast*/ true );
56355600
56365601 if (idxRng.IsConstantRange () && lenRng.IsConstantRange ())
0 commit comments