Skip to content

Commit a1eb855

Browse files
authored
RangeCheck: Don't create invalid ranges (#113935)
1 parent 6df065d commit a1eb855

File tree

2 files changed

+67
-5
lines changed

2 files changed

+67
-5
lines changed

src/coreclr/jit/rangecheck.cpp

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ int RangeCheck::GetArrLength(ValueNum vn)
143143
bool RangeCheck::BetweenBounds(Range& range, GenTree* upper, int arrSize)
144144
{
145145
#ifdef DEBUG
146+
assert(range.IsValid());
146147
if (m_pCompiler->verbose)
147148
{
148149
printf("%s BetweenBounds <%d, ", range.ToString(m_pCompiler), 0);
@@ -354,6 +355,8 @@ void RangeCheck::OptimizeRangeCheck(BasicBlock* block, Statement* stmt, GenTree*
354355
Range arrLenRange = GetRangeWorker(block, bndsChk->GetArrayLength(), false DEBUGARG(0));
355356
if (arrLenRange.LowerLimit().IsConstant())
356357
{
358+
assert(arrLenRange.IsValid());
359+
357360
// Lower known limit of ArrLen:
358361
const int lenLowerLimit = arrLenRange.LowerLimit().GetConstant();
359362

@@ -394,6 +397,8 @@ void RangeCheck::OptimizeRangeCheck(BasicBlock* block, Statement* stmt, GenTree*
394397
return;
395398
}
396399

400+
assert(range.IsValid());
401+
397402
// If upper or lower limit is found to be unknown (top), or it was found to
398403
// be unknown because of over budget or a deep search, then return early.
399404
if (range.UpperLimit().IsUnknown() || range.LowerLimit().IsUnknown())
@@ -638,6 +643,7 @@ void RangeCheck::MergeEdgeAssertions(GenTreeLclVarCommon* lcl, ASSERT_VALARG_TP
638643
bool RangeCheck::TryGetRangeFromAssertions(Compiler* comp, ValueNum num, ASSERT_VALARG_TP assertions, Range* pRange)
639644
{
640645
MergeEdgeAssertions(comp, num, ValueNumStore::NoVN, assertions, pRange, false);
646+
assert(pRange->IsValid());
641647
return !pRange->LowerLimit().IsUnknown() || !pRange->UpperLimit().IsUnknown();
642648
}
643649

@@ -1003,13 +1009,29 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp,
10031009
unreached();
10041010
};
10051011

1012+
if (!assertedRange.IsValid())
1013+
{
1014+
JITDUMP("assertedRange is invalid: [%s] - bail out\n", assertedRange.ToString(comp));
1015+
return;
1016+
}
1017+
10061018
JITDUMP("Tightening pRange: [%s] with assertedRange: [%s] into ", pRange->ToString(comp),
10071019
assertedRange.ToString(comp));
10081020

1009-
pRange->lLimit = tightenLimit(assertedRange.lLimit, pRange->lLimit, preferredBoundVN, true);
1010-
pRange->uLimit = tightenLimit(assertedRange.uLimit, pRange->uLimit, preferredBoundVN, false);
1021+
Range copy = *pRange;
1022+
copy.lLimit = tightenLimit(assertedRange.lLimit, copy.lLimit, preferredBoundVN, true);
1023+
copy.uLimit = tightenLimit(assertedRange.uLimit, copy.uLimit, preferredBoundVN, false);
10111024

1012-
JITDUMP("[%s]\n", pRange->ToString(comp));
1025+
JITDUMP("[%s]\n", copy.ToString(comp));
1026+
if (copy.IsValid())
1027+
{
1028+
*pRange = copy;
1029+
}
1030+
else
1031+
{
1032+
JITDUMP("invalid range after tightening\n");
1033+
return;
1034+
}
10131035
}
10141036
}
10151037

@@ -1200,6 +1222,9 @@ Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool
12001222
op2Range = *op2RangeCached;
12011223
}
12021224

1225+
assert(op1Range.IsValid());
1226+
assert(op2Range.IsValid());
1227+
12031228
Range r = Range(Limit::keUnknown);
12041229
if (binop->OperIs(GT_ADD))
12051230
{
@@ -1227,6 +1252,13 @@ Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool
12271252
JITDUMP("Right shift range: %s >> %s = %s\n", op1Range.ToString(m_pCompiler), op2Range.ToString(m_pCompiler),
12281253
r.ToString(m_pCompiler));
12291254
}
1255+
1256+
// Some binops may produce invalid ranges, e.g. <0, 1> * <-1, -1> = <0, -1>
1257+
if (!r.IsValid())
1258+
{
1259+
JITDUMP("BinOp range is invalid: %s\n", r.ToString(m_pCompiler));
1260+
return Range(Limit::keUnknown);
1261+
}
12301262
return r;
12311263
}
12321264

@@ -1715,6 +1747,7 @@ bool RangeCheck::TryGetRange(BasicBlock* block, GenTree* expr, Range* pRange)
17151747
ClearSearchPath();
17161748

17171749
Range range = GetRangeWorker(block, expr, false DEBUGARG(0));
1750+
assert(range.IsValid());
17181751
if (range.UpperLimit().IsUnknown() && range.LowerLimit().IsUnknown())
17191752
{
17201753
JITDUMP("Range is completely unknown.\n");

src/coreclr/jit/rangecheck.h

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ struct Limit
217217
return false;
218218
}
219219
#ifdef DEBUG
220-
const char* ToString(Compiler* comp)
220+
const char* ToString(Compiler* comp) const
221221
{
222222
switch (type)
223223
{
@@ -283,11 +283,34 @@ struct Range
283283
}
284284

285285
#ifdef DEBUG
286-
const char* ToString(Compiler* comp)
286+
const char* ToString(Compiler* comp) const
287287
{
288288
return comp->printfAlloc("<%s, %s>", lLimit.ToString(comp), uLimit.ToString(comp));
289289
}
290290
#endif
291+
292+
bool IsValid() const
293+
{
294+
// A valid range must have lower limit <= upper limit.
295+
if (lLimit.IsConstant() && uLimit.IsConstant())
296+
{
297+
return lLimit.GetConstant() <= uLimit.GetConstant();
298+
}
299+
300+
// When both limits are BinOpArray, we check if their offsets are valid
301+
if (lLimit.IsBinOpArray() && uLimit.IsBinOpArray() && lLimit.vn == uLimit.vn)
302+
{
303+
return lLimit.GetConstant() <= uLimit.GetConstant();
304+
}
305+
306+
// e.g. <$bnd + 10, 5> is not a valid range since $bnd is expected to be >= 0
307+
if (lLimit.IsBinOpArray() && uLimit.IsConstant())
308+
{
309+
return lLimit.GetConstant() <= uLimit.GetConstant();
310+
}
311+
312+
return true;
313+
}
291314
};
292315

293316
// Helpers for operations performed on ranges
@@ -452,6 +475,9 @@ struct RangeOps
452475
// then ignore the dependent variables for the lower bound but not for the upper bound.
453476
static Range Merge(const Range& r1, const Range& r2, bool monIncreasing)
454477
{
478+
assert(r1.IsValid());
479+
assert(r2.IsValid());
480+
455481
const Limit& r1lo = r1.LowerLimit();
456482
const Limit& r1hi = r1.UpperLimit();
457483
const Limit& r2lo = r2.LowerLimit();
@@ -639,6 +665,9 @@ struct RangeOps
639665
//
640666
static RelationKind EvalRelop(const genTreeOps relop, bool isUnsigned, const Range& x, const Range& y)
641667
{
668+
assert(x.IsValid());
669+
assert(y.IsValid());
670+
642671
const Limit& xLower = x.LowerLimit();
643672
const Limit& yLower = y.LowerLimit();
644673
const Limit& xUpper = x.UpperLimit();

0 commit comments

Comments
 (0)