Skip to content

Commit 9c7434b

Browse files
authored
Remove redundant bounds checks around arr[arr.Length - cns] pattern (#116105)
1 parent d4ca2ce commit 9c7434b

File tree

2 files changed

+49
-54
lines changed

2 files changed

+49
-54
lines changed

src/coreclr/jit/assertionprop.cpp

Lines changed: 47 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5311,6 +5311,50 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree
53115311
}
53125312
#endif // FEATURE_ENABLE_NO_RANGE_CHECKS
53135313

5314+
GenTreeBoundsChk* arrBndsChk = tree->AsBoundsChk();
5315+
ValueNum vnCurIdx = vnStore->VNConservativeNormalValue(arrBndsChk->GetIndex()->gtVNPair);
5316+
ValueNum vnCurLen = vnStore->VNConservativeNormalValue(arrBndsChk->GetArrayLength()->gtVNPair);
5317+
5318+
auto dropBoundsCheck = [&](INDEBUG(const char* reason)) -> GenTree* {
5319+
JITDUMP("\nVN based redundant (%s) bounds check assertion prop in " FMT_BB ":\n", reason, compCurBB->bbNum);
5320+
DISPTREE(tree);
5321+
if (arrBndsChk != stmt->GetRootNode())
5322+
{
5323+
// Defer the removal.
5324+
arrBndsChk->gtFlags |= GTF_CHK_INDEX_INBND;
5325+
return nullptr;
5326+
}
5327+
5328+
GenTree* newTree = optRemoveStandaloneRangeCheck(arrBndsChk, stmt);
5329+
return optAssertionProp_Update(newTree, arrBndsChk, stmt);
5330+
};
5331+
5332+
// First, check if we have arr[arr.Length - cns] when we know arr.Length is >= cns.
5333+
VNFuncApp funcApp;
5334+
if (vnStore->GetVNFunc(vnCurIdx, &funcApp) && (funcApp.m_func == VNF_ADD))
5335+
{
5336+
if (!vnStore->IsVNInt32Constant(funcApp.m_args[1]))
5337+
{
5338+
// Normalize constants to be on the right side
5339+
std::swap(funcApp.m_args[0], funcApp.m_args[1]);
5340+
}
5341+
5342+
Range rng = Range(Limit(Limit::keUnknown));
5343+
if ((funcApp.m_args[0] == vnCurLen) && vnStore->IsVNInt32Constant(funcApp.m_args[1]) &&
5344+
RangeCheck::TryGetRangeFromAssertions(this, vnCurLen, assertions, &rng) && rng.LowerLimit().IsConstant())
5345+
{
5346+
// Lower known limit of ArrLen:
5347+
const int lenLowerLimit = rng.LowerLimit().GetConstant();
5348+
5349+
// Negative delta in the array access (ArrLen + -CNS)
5350+
const int delta = vnStore->GetConstantInt32(funcApp.m_args[1]);
5351+
if ((lenLowerLimit > 0) && (delta < 0) && (delta > INT_MIN) && (lenLowerLimit >= -delta))
5352+
{
5353+
return dropBoundsCheck(INDEBUG("a[a.Length-cns] when a.Length is known to be >= cns"));
5354+
}
5355+
}
5356+
}
5357+
53145358
BitVecOps::Iter iter(apTraits, assertions);
53155359
unsigned index = 0;
53165360
while (iter.NextElem(&index))
@@ -5327,38 +5371,21 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree
53275371
continue;
53285372
}
53295373

5330-
GenTreeBoundsChk* arrBndsChk = tree->AsBoundsChk();
5331-
5332-
// Set 'isRedundant' to true if we can determine that 'arrBndsChk' can be
5333-
// classified as a redundant bounds check using 'curAssertion'
5334-
bool isRedundant = false;
5335-
#ifdef DEBUG
5336-
const char* dbgMsg = "Not Set";
5337-
#endif
5338-
53395374
// Do we have a previous range check involving the same 'vnLen' upper bound?
53405375
if (curAssertion->op1.bnd.vnLen == vnStore->VNConservativeNormalValue(arrBndsChk->GetArrayLength()->gtVNPair))
53415376
{
5342-
ValueNum vnCurIdx = vnStore->VNConservativeNormalValue(arrBndsChk->GetIndex()->gtVNPair);
5343-
53445377
// Do we have the exact same lower bound 'vnIdx'?
53455378
// a[i] followed by a[i]
53465379
if (curAssertion->op1.bnd.vnIdx == vnCurIdx)
53475380
{
5348-
isRedundant = true;
5349-
#ifdef DEBUG
5350-
dbgMsg = "a[i] followed by a[i]";
5351-
#endif
5381+
return dropBoundsCheck(INDEBUG("a[i] followed by a[i]"));
53525382
}
53535383
// Are we using zero as the index?
53545384
// It can always be considered as redundant with any previous value
53555385
// a[*] followed by a[0]
53565386
else if (vnCurIdx == vnStore->VNZeroForType(arrBndsChk->GetIndex()->TypeGet()))
53575387
{
5358-
isRedundant = true;
5359-
#ifdef DEBUG
5360-
dbgMsg = "a[*] followed by a[0]";
5361-
#endif
5388+
return dropBoundsCheck(INDEBUG("a[*] followed by a[0]"));
53625389
}
53635390
// Do we have two constant indexes?
53645391
else if (vnStore->IsVNConstant(curAssertion->op1.bnd.vnIdx) && vnStore->IsVNConstant(vnCurIdx))
@@ -5379,10 +5406,7 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree
53795406
// a[K1] followed by a[K2], with K2 >= 0 and K1 >= K2
53805407
if (index2 >= 0 && index1 >= index2)
53815408
{
5382-
isRedundant = true;
5383-
#ifdef DEBUG
5384-
dbgMsg = "a[K1] followed by a[K2], with K2 >= 0 and K1 >= K2";
5385-
#endif
5409+
return dropBoundsCheck(INDEBUG("a[K1] followed by a[K2], with K2 >= 0 and K1 >= K2"));
53865410
}
53875411
}
53885412
}
@@ -5391,35 +5415,6 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree
53915415
// a[i] followed by a[j] when j is known to be >= i
53925416
// a[i] followed by a[5] when i is known to be >= 5
53935417
}
5394-
5395-
if (!isRedundant)
5396-
{
5397-
continue;
5398-
}
5399-
5400-
#ifdef DEBUG
5401-
if (verbose)
5402-
{
5403-
printf("\nVN based redundant (%s) bounds check assertion prop for index #%02u in " FMT_BB ":\n", dbgMsg,
5404-
assertionIndex, compCurBB->bbNum);
5405-
gtDispTree(tree, nullptr, nullptr, true);
5406-
}
5407-
#endif
5408-
if (arrBndsChk == stmt->GetRootNode())
5409-
{
5410-
// We have a top-level bounds check node.
5411-
// This can happen when trees are broken up due to inlining.
5412-
// optRemoveStandaloneRangeCheck will return the modified tree (side effects or a no-op).
5413-
GenTree* newTree = optRemoveStandaloneRangeCheck(arrBndsChk, stmt);
5414-
5415-
return optAssertionProp_Update(newTree, arrBndsChk, stmt);
5416-
}
5417-
5418-
// Defer actually removing the tree until processing reaches its parent comma, since
5419-
// optRemoveCommaBasedRangeCheck needs to rewrite the whole comma tree.
5420-
arrBndsChk->gtFlags |= GTF_CHK_INDEX_INBND;
5421-
5422-
return nullptr;
54235418
}
54245419

54255420
return nullptr;

src/coreclr/jit/rangecheck.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -805,11 +805,11 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp,
805805
}
806806
else if ((normalLclVN == lenVN) && comp->vnStore->IsVNInt32Constant(indexVN))
807807
{
808-
// We have "Const < arr.Length" assertion, it means that "arr.Length >= Const"
808+
// We have "Const < arr.Length" assertion, it means that "arr.Length > Const"
809809
int indexCns = comp->vnStore->GetConstantInt32(indexVN);
810810
if (indexCns >= 0)
811811
{
812-
cmpOper = GT_GE;
812+
cmpOper = GT_GT;
813813
limit = Limit(Limit::keConstant, indexCns);
814814
}
815815
else

0 commit comments

Comments
 (0)