@@ -353,7 +353,7 @@ void RangeCheck::OptimizeRangeCheck(BasicBlock* block, Statement* stmt, GenTree*
353
353
return ;
354
354
}
355
355
356
- if (DoesOverflow (block, treeIndex))
356
+ if (DoesOverflow (block, treeIndex, range ))
357
357
{
358
358
JITDUMP (" Method determined to overflow.\n " );
359
359
return ;
@@ -773,6 +773,22 @@ void RangeCheck::MergeEdgeAssertions(ValueNum normalLclVN, ASSERT_VALARG_TP asse
773
773
774
774
isConstantAssertion = true ;
775
775
}
776
+ // Current assertion asserts a bounds check does not throw
777
+ else if (curAssertion->IsBoundsCheckNoThrow ())
778
+ {
779
+ ValueNum indexVN = curAssertion->op1 .bnd .vnIdx ;
780
+ ValueNum lenVN = curAssertion->op1 .bnd .vnLen ;
781
+ if (normalLclVN == indexVN)
782
+ {
783
+ isUnsigned = true ;
784
+ cmpOper = GT_LT;
785
+ limit = Limit (Limit::keBinOpArray, lenVN, 0 );
786
+ }
787
+ else
788
+ {
789
+ continue ;
790
+ }
791
+ }
776
792
// Current assertion is not supported, ignore it
777
793
else
778
794
{
@@ -782,7 +798,8 @@ void RangeCheck::MergeEdgeAssertions(ValueNum normalLclVN, ASSERT_VALARG_TP asse
782
798
assert (limit.IsBinOpArray () || limit.IsConstant ());
783
799
784
800
// Make sure the assertion is of the form != 0 or == 0 if it isn't a constant assertion.
785
- if (!isConstantAssertion && (curAssertion->op2 .vn != m_pCompiler->vnStore ->VNZeroForType (TYP_INT)))
801
+ if (!isConstantAssertion && (curAssertion->assertionKind != Compiler::OAK_NO_THROW) &&
802
+ (curAssertion->op2 .vn != m_pCompiler->vnStore ->VNZeroForType (TYP_INT)))
786
803
{
787
804
continue ;
788
805
}
@@ -1235,17 +1252,17 @@ bool RangeCheck::MultiplyOverflows(Limit& limit1, Limit& limit2)
1235
1252
}
1236
1253
1237
1254
// Does the bin operation overflow.
1238
- bool RangeCheck::DoesBinOpOverflow (BasicBlock* block, GenTreeOp* binop)
1255
+ bool RangeCheck::DoesBinOpOverflow (BasicBlock* block, GenTreeOp* binop, const Range& range )
1239
1256
{
1240
1257
GenTree* op1 = binop->gtGetOp1 ();
1241
1258
GenTree* op2 = binop->gtGetOp2 ();
1242
1259
1243
- if (!m_pSearchPath->Lookup (op1) && DoesOverflow (block, op1))
1260
+ if (!m_pSearchPath->Lookup (op1) && DoesOverflow (block, op1, range ))
1244
1261
{
1245
1262
return true ;
1246
1263
}
1247
1264
1248
- if (!m_pSearchPath->Lookup (op2) && DoesOverflow (block, op2))
1265
+ if (!m_pSearchPath->Lookup (op2) && DoesOverflow (block, op2, range ))
1249
1266
{
1250
1267
return true ;
1251
1268
}
@@ -1279,7 +1296,7 @@ bool RangeCheck::DoesBinOpOverflow(BasicBlock* block, GenTreeOp* binop)
1279
1296
}
1280
1297
1281
1298
// Check if the var definition the rhs involves arithmetic that overflows.
1282
- bool RangeCheck::DoesVarDefOverflow (GenTreeLclVarCommon* lcl)
1299
+ bool RangeCheck::DoesVarDefOverflow (BasicBlock* block, GenTreeLclVarCommon* lcl, const Range& range )
1283
1300
{
1284
1301
LclSsaVarDsc* ssaDef = GetSsaDefStore (lcl);
1285
1302
if (ssaDef == nullptr )
@@ -1291,10 +1308,25 @@ bool RangeCheck::DoesVarDefOverflow(GenTreeLclVarCommon* lcl)
1291
1308
}
1292
1309
return true ;
1293
1310
}
1294
- return DoesOverflow (ssaDef->GetBlock (), ssaDef->GetDefNode ()->Data ());
1311
+
1312
+ // We can use intermediate assertions about the local to prove that any
1313
+ // overflow on this path does not matter for the range computed.
1314
+ Range assertionRange = Range (Limit (Limit::keUnknown));
1315
+ MergeAssertion (block, lcl, &assertionRange DEBUGARG (0 ));
1316
+
1317
+ // But only if the range from the assertion is more strict than the global
1318
+ // range computed; otherwise we might still have used the def's value to
1319
+ // tighten the range of the global range.
1320
+ Range merged = RangeOps::Merge (range, assertionRange, false );
1321
+ if (merged.LowerLimit ().Equals (range.LowerLimit ()) && merged.UpperLimit ().Equals (range.UpperLimit ()))
1322
+ {
1323
+ return false ;
1324
+ }
1325
+
1326
+ return DoesOverflow (ssaDef->GetBlock (), ssaDef->GetDefNode ()->Data (), range);
1295
1327
}
1296
1328
1297
- bool RangeCheck::DoesPhiOverflow (BasicBlock* block, GenTree* expr)
1329
+ bool RangeCheck::DoesPhiOverflow (BasicBlock* block, GenTree* expr, const Range& range )
1298
1330
{
1299
1331
for (GenTreePhi::Use& use : expr->AsPhi ()->Uses ())
1300
1332
{
@@ -1303,25 +1335,38 @@ bool RangeCheck::DoesPhiOverflow(BasicBlock* block, GenTree* expr)
1303
1335
{
1304
1336
continue ;
1305
1337
}
1306
- if (DoesOverflow (block, arg))
1338
+ if (DoesOverflow (block, arg, range ))
1307
1339
{
1308
1340
return true ;
1309
1341
}
1310
1342
}
1311
1343
return false ;
1312
1344
}
1313
1345
1314
- bool RangeCheck::DoesOverflow (BasicBlock* block, GenTree* expr)
1346
+ // ------------------------------------------------------------------------
1347
+ // DoesOverflow: Check if the computation of "expr" may have overflowed.
1348
+ //
1349
+ // Arguments:
1350
+ // block - the block that contains `expr`
1351
+ // expr - expression to check overflow of
1352
+ // range - range that we believe "expr" to be in without accounting for
1353
+ // overflow; used to ignore potential overflow on paths where
1354
+ // we can prove the value is in this range regardless.
1355
+ //
1356
+ // Return value:
1357
+ // True if the computation may have involved an impactful overflow.
1358
+ //
1359
+ bool RangeCheck::DoesOverflow (BasicBlock* block, GenTree* expr, const Range& range)
1315
1360
{
1316
1361
bool overflows = false ;
1317
1362
if (!GetOverflowMap ()->Lookup (expr, &overflows))
1318
1363
{
1319
- overflows = ComputeDoesOverflow (block, expr);
1364
+ overflows = ComputeDoesOverflow (block, expr, range );
1320
1365
}
1321
1366
return overflows;
1322
1367
}
1323
1368
1324
- bool RangeCheck::ComputeDoesOverflow (BasicBlock* block, GenTree* expr)
1369
+ bool RangeCheck::ComputeDoesOverflow (BasicBlock* block, GenTree* expr, const Range& range )
1325
1370
{
1326
1371
JITDUMP (" Does overflow [%06d]?\n " , Compiler::dspTreeID (expr));
1327
1372
m_pSearchPath->Set (expr, block, SearchPath::Overwrite);
@@ -1343,17 +1388,17 @@ bool RangeCheck::ComputeDoesOverflow(BasicBlock* block, GenTree* expr)
1343
1388
}
1344
1389
else if (expr->OperIs (GT_COMMA))
1345
1390
{
1346
- overflows = ComputeDoesOverflow (block, expr->gtEffectiveVal ());
1391
+ overflows = ComputeDoesOverflow (block, expr->gtEffectiveVal (), range );
1347
1392
}
1348
1393
// Check if the var def has rhs involving arithmetic that overflows.
1349
1394
else if (expr->IsLocal ())
1350
1395
{
1351
- overflows = DoesVarDefOverflow (expr->AsLclVarCommon ());
1396
+ overflows = DoesVarDefOverflow (block, expr->AsLclVarCommon (), range );
1352
1397
}
1353
1398
// Check if add overflows.
1354
1399
else if (expr->OperIs (GT_ADD, GT_MUL))
1355
1400
{
1356
- overflows = DoesBinOpOverflow (block, expr->AsOp ());
1401
+ overflows = DoesBinOpOverflow (block, expr->AsOp (), range );
1357
1402
}
1358
1403
// These operators don't overflow.
1359
1404
// Actually, GT_LSH can overflow so it depends on the analysis done in ComputeRangeForBinOp
@@ -1364,11 +1409,11 @@ bool RangeCheck::ComputeDoesOverflow(BasicBlock* block, GenTree* expr)
1364
1409
// Walk through phi arguments to check if phi arguments involve arithmetic that overflows.
1365
1410
else if (expr->OperIs (GT_PHI))
1366
1411
{
1367
- overflows = DoesPhiOverflow (block, expr);
1412
+ overflows = DoesPhiOverflow (block, expr, range );
1368
1413
}
1369
1414
else if (expr->OperIs (GT_CAST))
1370
1415
{
1371
- overflows = ComputeDoesOverflow (block, expr->gtGetOp1 ());
1416
+ overflows = ComputeDoesOverflow (block, expr->gtGetOp1 (), range );
1372
1417
}
1373
1418
GetOverflowMap ()->Set (expr, overflows, OverflowMap::Overwrite);
1374
1419
m_pSearchPath->Remove (expr);
0 commit comments