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
117 changes: 67 additions & 50 deletions src/coreclr/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3572,30 +3572,6 @@ void Compiler::optAssertionProp_RangeProperties(ASSERT_VALARG_TP assertions,
{
const AssertionDsc& curAssertion = optGetAssertion(GetAssertionIndex(index));

// if treeVN has a bound-check assertion where it's an index, then
// it means it's not negative, example:
//
// array[idx] = 42; // creates 'BoundsCheckNoThrow' assertion
// return idx % 8; // idx is known to be never negative here, hence, MOD->UMOD
//
if (curAssertion.IsBoundsCheckNoThrow() && (curAssertion.GetOp1().GetVN() == treeVN))
{
*isKnownNonNegative = true;
continue;
}

// Same for Length, example:
//
// array[idx] = 42;
// array.Length is known to be non-negative and non-zero here
//
if (curAssertion.IsBoundsCheckNoThrow() && (curAssertion.GetOp2().GetCheckedBound() == treeVN))
{
*isKnownNonNegative = true;
*isKnownNonZero = true;
return; // both properties are known, no need to check other assertions
}

// First, analyze possible X ==/!= CNS assertions.
if (curAssertion.IsConstantInt32Assertion() && (curAssertion.GetOp1().GetVN() == treeVN))
{
Expand Down Expand Up @@ -3637,14 +3613,15 @@ void Compiler::optAssertionProp_RangeProperties(ASSERT_VALARG_TP assertions,
}

// Let's see if MergeEdgeAssertions can help us:
if (tree->TypeIs(TYP_INT))
if (genActualType(tree) == TYP_INT)
{
Range rng = RangeCheck::GetRangeFromAssertions(this, treeVN, assertions);
assert(rng.IsConstantRange());
if (rng.LowerLimit().GetConstant() >= 0)
{
*isKnownNonNegative = true;
}
if (rng.LowerLimit().GetConstant() > 0)
if ((rng.LowerLimit().GetConstant() > 0) || (rng.UpperLimit().GetConstant() < 0))
{
*isKnownNonZero = true;
}
Expand Down Expand Up @@ -4430,21 +4407,55 @@ GenTree* Compiler::optAssertionProp_Cast(ASSERT_VALARG_TP assertions,
}
}

// If we don't have a cast of a LCL_VAR then bail.
if (!lcl->OperIs(GT_LCL_VAR))
bool removeCast = false;
if (!optLocalAssertionProp)
{
return nullptr;
}
// Get the non-overflowing input range for a cast. ForCastInput takes care of special cases like
// small types and IsUnsigned flag for checked casts.
IntegralRange castRng = IntegralRange::ForCastInput(cast);
int64_t castLo = IntegralRange::SymbolicToRealValue(castRng.GetLowerBound());
int64_t castHi = IntegralRange::SymbolicToRealValue(castRng.GetUpperBound());
if (FitsIn<int>(castLo) && FitsIn<int>(castHi))
{
Range castToTypeRange = Range(Limit(Limit::keConstant, (int)castLo), Limit(Limit::keConstant, (int)castHi));
if (castToTypeRange.IsConstantRange() && (genActualType(cast->CastOp()) == TYP_INT))
{
ValueNum castOpVN = optConservativeNormalVN(cast->CastOp());
Range castOpRng = RangeCheck::GetRangeFromAssertions(this, castOpVN, assertions);
assert(castOpRng.IsConstantRange());

if (!optLocalAssertionProp)
int castFromLo = castOpRng.LowerLimit().GetConstant();
int castFromHi = castOpRng.UpperLimit().GetConstant();
int castToLo = castToTypeRange.LowerLimit().GetConstant();
int castToHi = castToTypeRange.UpperLimit().GetConstant();

if (castOpRng.IsConstantRange() && (castFromLo >= castToLo) && (castFromHi <= castToHi))
{
removeCast = true;
if (!lcl->OperIs(GT_LCL_VAR))
{
// We cannot remove the cast, but can we just remove the GTF_OVERFLOW flag?
if (!cast->gtOverflow())
{
return nullptr;
}

// Just clear the overflow flag then.
JITDUMP("Clearing overflow flag for cast %06u based on assertions.\n", dspTreeID(cast));
cast->ClearOverflow();
return optAssertionProp_Update(cast, cast, stmt);
}
}
}
}
}
else
{
// optAssertionIsSubrange is only for local assertion prop.
return nullptr;
removeCast = lcl->OperIs(GT_LCL_VAR) &&
optAssertionIsSubrange(lcl, IntegralRange::ForCastInput(cast), assertions) != NO_ASSERTION_INDEX;
}

IntegralRange range = IntegralRange::ForCastInput(cast);
AssertionIndex index = optAssertionIsSubrange(lcl, range, assertions);
if (index != NO_ASSERTION_INDEX)
if (removeCast)
{
LclVarDsc* varDsc = lvaGetDesc(lcl->AsLclVarCommon());

Expand All @@ -4456,13 +4467,8 @@ GenTree* Compiler::optAssertionProp_Cast(ASSERT_VALARG_TP assertions,
{
return nullptr;
}
#ifdef DEBUG
if (verbose)
{
printf("\nSubrange prop for index #%02u in " FMT_BB ":\n", index, compCurBB->bbNum);
DISPNODE(cast);
}
#endif

JITDUMP("Clearing overflow flag for cast %06u based on assertions.\n", dspTreeID(cast));
cast->ClearOverflow();
return optAssertionProp_Update(cast, cast, stmt);
}
Expand All @@ -4481,13 +4487,7 @@ GenTree* Compiler::optAssertionProp_Cast(ASSERT_VALARG_TP assertions,
op1->ChangeType(varDsc->TypeGet());
}

#ifdef DEBUG
if (verbose)
{
printf("\nSubrange prop for index #%02u in " FMT_BB ":\n", index, compCurBB->bbNum);
DISPNODE(cast);
}
#endif
JITDUMP("Removing cast %06u as redundant based on assertions.\n", dspTreeID(cast));
return optAssertionProp_Update(op1, cast, stmt);
}

Expand Down Expand Up @@ -5090,6 +5090,23 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree
}
}

// Let's see if we can remove the bounds check based on the ranges.
if ((genActualType(vnStore->TypeOfVN(vnCurIdx)) == TYP_INT) &&
(genActualType(vnStore->TypeOfVN(vnCurLen)) == TYP_INT))
{
Range idxRng = RangeCheck::GetRangeFromAssertions(this, vnCurIdx, assertions);
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())
{
return dropBoundsCheck(INDEBUG("upper bound of index is less than lower bound of length"));
}
}
}

return nullptr;
}

Expand Down
24 changes: 17 additions & 7 deletions src/coreclr/jit/rangecheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1056,18 +1056,28 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp,
}
}
}
else if ((normalLclVN == lenVN) && comp->vnStore->IsVNInt32Constant(indexVN))
else if (normalLclVN == lenVN)
{
// We have "Const < arr.Length" assertion, it means that "arr.Length > Const"
int indexCns = comp->vnStore->GetConstantInt32(indexVN);
if (indexCns >= 0)
if (comp->vnStore->IsVNInt32Constant(indexVN))
{
cmpOper = GT_GT;
limit = Limit(Limit::keConstant, indexCns);
// We have "Const < arr.Length" assertion, it means that "arr.Length > Const"
int indexCns = comp->vnStore->GetConstantInt32(indexVN);
if (indexCns >= 0)
{
cmpOper = GT_GT;
limit = Limit(Limit::keConstant, indexCns);
}
else
{
continue;
}
}
else
{
continue;
// We've seen arr[unknown_index] assertion while normalLclVN == arr.Length.
// This means the array has at least one element, so we can deduce "normalLclVN > 0".
cmpOper = GT_GT;
limit = Limit(Limit::keConstant, 0);
}
}
else
Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/jit/rangecheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,9 @@ class RangeCheck
// Cheaper version of TryGetRange that is based only on incoming assertions.
static Range GetRangeFromAssertions(Compiler* comp, ValueNum num, ASSERT_VALARG_TP assertions, int budget = 10);

// Compute the range from the given type
static Range GetRangeFromType(var_types type);

private:
typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, bool> OverflowMap;
typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, Range*> RangeMap;
Expand All @@ -782,9 +785,6 @@ class RangeCheck
// Internal worker for GetRange.
Range GetRangeWorker(BasicBlock* block, GenTree* expr, bool monIncreasing DEBUGARG(int indent));

// Compute the range from the given type
static Range GetRangeFromType(var_types type);

// Given the local variable, first find the definition of the local and find the range of the rhs.
// Helper for GetRangeWorker.
Range ComputeRangeForLocalDef(BasicBlock* block, GenTreeLclVarCommon* lcl, bool monIncreasing DEBUGARG(int indent));
Expand Down
Loading
Loading