@@ -5311,6 +5311,50 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree
5311
5311
}
5312
5312
#endif // FEATURE_ENABLE_NO_RANGE_CHECKS
5313
5313
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 (" \n VN 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
+
5314
5358
BitVecOps::Iter iter (apTraits, assertions);
5315
5359
unsigned index = 0 ;
5316
5360
while (iter.NextElem (&index))
@@ -5327,38 +5371,21 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree
5327
5371
continue ;
5328
5372
}
5329
5373
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
-
5339
5374
// Do we have a previous range check involving the same 'vnLen' upper bound?
5340
5375
if (curAssertion->op1 .bnd .vnLen == vnStore->VNConservativeNormalValue (arrBndsChk->GetArrayLength ()->gtVNPair ))
5341
5376
{
5342
- ValueNum vnCurIdx = vnStore->VNConservativeNormalValue (arrBndsChk->GetIndex ()->gtVNPair );
5343
-
5344
5377
// Do we have the exact same lower bound 'vnIdx'?
5345
5378
// a[i] followed by a[i]
5346
5379
if (curAssertion->op1 .bnd .vnIdx == vnCurIdx)
5347
5380
{
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]" ));
5352
5382
}
5353
5383
// Are we using zero as the index?
5354
5384
// It can always be considered as redundant with any previous value
5355
5385
// a[*] followed by a[0]
5356
5386
else if (vnCurIdx == vnStore->VNZeroForType (arrBndsChk->GetIndex ()->TypeGet ()))
5357
5387
{
5358
- isRedundant = true ;
5359
- #ifdef DEBUG
5360
- dbgMsg = " a[*] followed by a[0]" ;
5361
- #endif
5388
+ return dropBoundsCheck (INDEBUG (" a[*] followed by a[0]" ));
5362
5389
}
5363
5390
// Do we have two constant indexes?
5364
5391
else if (vnStore->IsVNConstant (curAssertion->op1 .bnd .vnIdx ) && vnStore->IsVNConstant (vnCurIdx))
@@ -5379,10 +5406,7 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree
5379
5406
// a[K1] followed by a[K2], with K2 >= 0 and K1 >= K2
5380
5407
if (index2 >= 0 && index1 >= index2)
5381
5408
{
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" ));
5386
5410
}
5387
5411
}
5388
5412
}
@@ -5391,35 +5415,6 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree
5391
5415
// a[i] followed by a[j] when j is known to be >= i
5392
5416
// a[i] followed by a[5] when i is known to be >= 5
5393
5417
}
5394
-
5395
- if (!isRedundant)
5396
- {
5397
- continue ;
5398
- }
5399
-
5400
- #ifdef DEBUG
5401
- if (verbose)
5402
- {
5403
- printf (" \n VN 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 ;
5423
5418
}
5424
5419
5425
5420
return nullptr ;
0 commit comments