Skip to content
26 changes: 26 additions & 0 deletions src/coreclr/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4611,6 +4611,32 @@ GenTree* Compiler::optAssertionPropGlobal_RelOp(ASSERT_VALARG_TP assertions,
return optAssertionProp_Update(newTree, tree, stmt);
}

ValueNum op1VN = vnStore->VNConservativeNormalValue(op1->gtVNPair);
ValueNum op2VN = vnStore->VNConservativeNormalValue(op2->gtVNPair);

// See if we can fold "X relop CNS" using TryGetRangeFromAssertions.
int op2cns;
if (op1->TypeIs(TYP_INT) && op2->TypeIs(TYP_INT) &&
vnStore->IsVNIntegralConstant(op2VN, &op2cns)
// "op2cns != 0" is purely a TP quirk (such relops are handled by the code above):
&& (op2cns != 0))
{
// NOTE: we can call TryGetRangeFromAssertions for op2 as well if we want, but it's not cheap.
Range rng1 = Range(Limit(Limit::keUndef));
Range rng2 = Range(Limit(Limit::keConstant, op2cns));

if (RangeCheck::TryGetRangeFromAssertions(this, op1VN, assertions, &rng1))
{
RangeOps::RelationKind kind = RangeOps::EvalRelop(tree->OperGet(), tree->IsUnsigned(), rng1, rng2);
if ((kind != RangeOps::RelationKind::Unknown))
{
newTree = kind == RangeOps::RelationKind::AlwaysTrue ? gtNewTrue() : gtNewFalse();
newTree = gtWrapWithSideEffects(newTree, tree, GTF_ALL_EFFECT);
return optAssertionProp_Update(newTree, tree, stmt);
}
}
}

// Else check if we have an equality check involving a local or an indir
if (!tree->OperIs(GT_EQ, GT_NE))
{
Expand Down
19 changes: 19 additions & 0 deletions src/coreclr/jit/rangecheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,20 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp,
cmpOper = GT_LT;
limit = Limit(Limit::keBinOpArray, lenVN, 0);
}
else if ((normalLclVN == lenVN) && comp->vnStore->IsVNInt32Constant(indexVN))
{
// We have "Const < arr.Length" assertion, it means that "arr.Length >= Const"
int indexCns = comp->vnStore->GetConstantInt32(indexVN);
if (indexCns >= 0)
{
cmpOper = GT_GE;
limit = Limit(Limit::keConstant, indexCns);
}
else
{
continue;
}
}
else
{
continue;
Expand Down Expand Up @@ -977,6 +991,11 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp,
if (!isUnsigned)
{
pRange->lLimit = limit;
// INT32_MAX as the upper limit is better than UNKNOWN for a constant lower limit.
if (limit.IsConstant() && pRange->UpperLimit().IsUnknown())
{
pRange->uLimit = Limit(Limit::keConstant, INT32_MAX);
}
}
break;

Expand Down
83 changes: 83 additions & 0 deletions src/coreclr/jit/rangecheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,89 @@ struct RangeOps
result.uLimit = Limit(Limit::keConstant, -lo);
return result;
}

enum class RelationKind
{
AlwaysTrue,
AlwaysFalse,
Unknown
};

//------------------------------------------------------------------------
// EvalRelop: Evaluate the relation between two ranges for the given relop
// Example: "x >= y" is AlwaysTrue when "x.LowerLimit() >= y.UpperLimit()"
//
// Arguments:
// relop - The relational operator (LE,LT,GE,GT,EQ,NE)
// isUnsigned - True if the comparison is unsigned
// x - The left range
// y - The right range
//
// Returns:
// AlwaysTrue when the given relop always evaluates to true for the given ranges
// AlwaysFalse when the given relop always evaluates to false for the given ranges
// Otherwise Unknown
//
static RelationKind EvalRelop(const genTreeOps relop, bool isUnsigned, const Range& x, const Range& y)
{
const Limit& xLower = x.LowerLimit();
const Limit& yLower = y.LowerLimit();
const Limit& xUpper = x.UpperLimit();
const Limit& yUpper = y.UpperLimit();

// For unsigned comparisons, we only support non-negative ranges.
if (isUnsigned)
{
if (!xLower.IsConstant() || !yUpper.IsConstant() || (xLower.GetConstant() < 0) ||
(yLower.GetConstant() < 0))
{
return RelationKind::Unknown;
}
}

switch (relop)
{
case GT_GE:
case GT_LT:
if (xLower.IsConstant() && yUpper.IsConstant() && (xLower.GetConstant() >= yUpper.GetConstant()))
{
return relop == GT_GE ? RelationKind::AlwaysTrue : RelationKind::AlwaysFalse;
}

if (xUpper.IsConstant() && yLower.IsConstant() && (xUpper.GetConstant() < yLower.GetConstant()))
{
return relop == GT_GE ? RelationKind::AlwaysFalse : RelationKind::AlwaysTrue;
}
break;

case GT_GT:
case GT_LE:
if (xLower.IsConstant() && yUpper.IsConstant() && (xLower.GetConstant() > yUpper.GetConstant()))
{
return relop == GT_GT ? RelationKind::AlwaysTrue : RelationKind::AlwaysFalse;
}

if (xUpper.IsConstant() && yLower.IsConstant() && (xUpper.GetConstant() <= yLower.GetConstant()))
{
return relop == GT_GT ? RelationKind::AlwaysFalse : RelationKind::AlwaysTrue;
}
break;

case GT_EQ:
case GT_NE:
if ((xLower.IsConstant() && yUpper.IsConstant() && (xLower.GetConstant() > yUpper.GetConstant())) ||
(xUpper.IsConstant() && yLower.IsConstant() && (xUpper.GetConstant() < yLower.GetConstant())))
{
return relop == GT_EQ ? RelationKind::AlwaysFalse : RelationKind::AlwaysTrue;
}
break;

default:
assert(!"unknown comparison operator");
break;
}
return RelationKind::Unknown;
}
};

class RangeCheck
Expand Down
Loading