Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions src/coreclr/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5332,9 +5332,31 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree
Range lenRng = RangeCheck::GetRangeFromAssertions(this, vnCurLen, assertions);
if (idxRng.IsConstantRange() && lenRng.IsConstantRange())
{
// idx.lo >= 0 && idx.hi < len.lo --> drop bounds check
if (idxRng.LowerLimit().GetConstant() >= 0 &&
idxRng.UpperLimit().GetConstant() < lenRng.LowerLimit().GetConstant())
int idxLo = idxRng.LowerLimit().GetConstant();
int idxHi = idxRng.UpperLimit().GetConstant();
int lenLo = lenRng.LowerLimit().GetConstant();

// GT_BOUNDS_CHECK node has an implicit contract - the length node must always be non-negative.
// So we additionally tighten the lower bound of lenLo to be ">= 1" when we also have a
// "length != 0" assertion for it.
if ((idxLo == 0) && (idxHi == 0) && (lenLo <= 0))
{
BitVecOps::Iter iter(apTraits, assertions);
unsigned bvIndex = 0;
while (iter.NextElem(&bvIndex))
{
const AssertionDsc& assertion = optGetAssertion(GetAssertionIndex(bvIndex));
if (assertion.IsConstantInt32Assertion() && assertion.KindIs(OAK_NOT_EQUAL) &&
(assertion.GetOp1().GetVN() == vnCurLen) && (assertion.GetOp2().GetIntConstant() == 0))
{
lenLo = 1;
break;
Comment thread
EgorBo marked this conversation as resolved.
}
}
}

// index is always within [0..lenLo) --> drop bounds check
if ((idxLo >= 0) && (idxHi < lenLo))
{
return dropBoundsCheck(INDEBUG("upper bound of index is less than lower bound of length"));
}
Expand Down
20 changes: 20 additions & 0 deletions src/tests/JIT/opt/RangeChecks/ElidedBoundsChecks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ static bool IndexPlusConstLessThanLen(ReadOnlySpan<char> span)
return false;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static bool TryStripFirstChar(ref ReadOnlySpan<char> span, char value)
{
// X64-NOT: CORINFO_HELP_RNGCHKFAIL
// ARM64-NOT: CORINFO_HELP_RNGCHKFAIL
if (!span.IsEmpty && span[0] == value)
{
return true;
}
return false;
}

[Fact]
public static int TestEntryPoint()
{
Expand Down Expand Up @@ -119,6 +131,14 @@ public static int TestEntryPoint()
if (IndexPlusConstLessThanLen("hello".AsSpan()) != false)
return 0;

ReadOnlySpan<char> chars = "hello".AsSpan();
if (TryStripFirstChar(ref chars, 'h') != true)
return 0;

chars = ReadOnlySpan<char>.Empty;
if (TryStripFirstChar(ref chars, 'h') != false)
return 0;

return 100;
}
}
Loading