Skip to content

Commit 4729bf9

Browse files
authored
Use TryGetRangeFromAssertions to fold more relops in global assert prop (#112824)
1 parent 9208fb6 commit 4729bf9

File tree

3 files changed

+128
-0
lines changed

3 files changed

+128
-0
lines changed

src/coreclr/jit/assertionprop.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4611,6 +4611,32 @@ GenTree* Compiler::optAssertionPropGlobal_RelOp(ASSERT_VALARG_TP assertions,
46114611
return optAssertionProp_Update(newTree, tree, stmt);
46124612
}
46134613

4614+
ValueNum op1VN = vnStore->VNConservativeNormalValue(op1->gtVNPair);
4615+
ValueNum op2VN = vnStore->VNConservativeNormalValue(op2->gtVNPair);
4616+
4617+
// See if we can fold "X relop CNS" using TryGetRangeFromAssertions.
4618+
int op2cns;
4619+
if (op1->TypeIs(TYP_INT) && op2->TypeIs(TYP_INT) &&
4620+
vnStore->IsVNIntegralConstant(op2VN, &op2cns)
4621+
// "op2cns != 0" is purely a TP quirk (such relops are handled by the code above):
4622+
&& (op2cns != 0))
4623+
{
4624+
// NOTE: we can call TryGetRangeFromAssertions for op2 as well if we want, but it's not cheap.
4625+
Range rng1 = Range(Limit(Limit::keUndef));
4626+
Range rng2 = Range(Limit(Limit::keConstant, op2cns));
4627+
4628+
if (RangeCheck::TryGetRangeFromAssertions(this, op1VN, assertions, &rng1))
4629+
{
4630+
RangeOps::RelationKind kind = RangeOps::EvalRelop(tree->OperGet(), tree->IsUnsigned(), rng1, rng2);
4631+
if ((kind != RangeOps::RelationKind::Unknown))
4632+
{
4633+
newTree = kind == RangeOps::RelationKind::AlwaysTrue ? gtNewTrue() : gtNewFalse();
4634+
newTree = gtWrapWithSideEffects(newTree, tree, GTF_ALL_EFFECT);
4635+
return optAssertionProp_Update(newTree, tree, stmt);
4636+
}
4637+
}
4638+
}
4639+
46144640
// Else check if we have an equality check involving a local or an indir
46154641
if (!tree->OperIs(GT_EQ, GT_NE))
46164642
{

src/coreclr/jit/rangecheck.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,20 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp,
801801
cmpOper = GT_LT;
802802
limit = Limit(Limit::keBinOpArray, lenVN, 0);
803803
}
804+
else if ((normalLclVN == lenVN) && comp->vnStore->IsVNInt32Constant(indexVN))
805+
{
806+
// We have "Const < arr.Length" assertion, it means that "arr.Length >= Const"
807+
int indexCns = comp->vnStore->GetConstantInt32(indexVN);
808+
if (indexCns >= 0)
809+
{
810+
cmpOper = GT_GE;
811+
limit = Limit(Limit::keConstant, indexCns);
812+
}
813+
else
814+
{
815+
continue;
816+
}
817+
}
804818
else
805819
{
806820
continue;
@@ -977,6 +991,11 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp,
977991
if (!isUnsigned)
978992
{
979993
pRange->lLimit = limit;
994+
// INT32_MAX as the upper limit is better than UNKNOWN for a constant lower limit.
995+
if (limit.IsConstant() && pRange->UpperLimit().IsUnknown())
996+
{
997+
pRange->uLimit = Limit(Limit::keConstant, INT32_MAX);
998+
}
980999
}
9811000
break;
9821001

src/coreclr/jit/rangecheck.h

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,89 @@ struct RangeOps
585585
result.uLimit = Limit(Limit::keConstant, -lo);
586586
return result;
587587
}
588+
589+
enum class RelationKind
590+
{
591+
AlwaysTrue,
592+
AlwaysFalse,
593+
Unknown
594+
};
595+
596+
//------------------------------------------------------------------------
597+
// EvalRelop: Evaluate the relation between two ranges for the given relop
598+
// Example: "x >= y" is AlwaysTrue when "x.LowerLimit() >= y.UpperLimit()"
599+
//
600+
// Arguments:
601+
// relop - The relational operator (LE,LT,GE,GT,EQ,NE)
602+
// isUnsigned - True if the comparison is unsigned
603+
// x - The left range
604+
// y - The right range
605+
//
606+
// Returns:
607+
// AlwaysTrue when the given relop always evaluates to true for the given ranges
608+
// AlwaysFalse when the given relop always evaluates to false for the given ranges
609+
// Otherwise Unknown
610+
//
611+
static RelationKind EvalRelop(const genTreeOps relop, bool isUnsigned, const Range& x, const Range& y)
612+
{
613+
const Limit& xLower = x.LowerLimit();
614+
const Limit& yLower = y.LowerLimit();
615+
const Limit& xUpper = x.UpperLimit();
616+
const Limit& yUpper = y.UpperLimit();
617+
618+
// For unsigned comparisons, we only support non-negative ranges.
619+
if (isUnsigned)
620+
{
621+
if (!xLower.IsConstant() || !yUpper.IsConstant() || (xLower.GetConstant() < 0) ||
622+
(yLower.GetConstant() < 0))
623+
{
624+
return RelationKind::Unknown;
625+
}
626+
}
627+
628+
switch (relop)
629+
{
630+
case GT_GE:
631+
case GT_LT:
632+
if (xLower.IsConstant() && yUpper.IsConstant() && (xLower.GetConstant() >= yUpper.GetConstant()))
633+
{
634+
return relop == GT_GE ? RelationKind::AlwaysTrue : RelationKind::AlwaysFalse;
635+
}
636+
637+
if (xUpper.IsConstant() && yLower.IsConstant() && (xUpper.GetConstant() < yLower.GetConstant()))
638+
{
639+
return relop == GT_GE ? RelationKind::AlwaysFalse : RelationKind::AlwaysTrue;
640+
}
641+
break;
642+
643+
case GT_GT:
644+
case GT_LE:
645+
if (xLower.IsConstant() && yUpper.IsConstant() && (xLower.GetConstant() > yUpper.GetConstant()))
646+
{
647+
return relop == GT_GT ? RelationKind::AlwaysTrue : RelationKind::AlwaysFalse;
648+
}
649+
650+
if (xUpper.IsConstant() && yLower.IsConstant() && (xUpper.GetConstant() <= yLower.GetConstant()))
651+
{
652+
return relop == GT_GT ? RelationKind::AlwaysFalse : RelationKind::AlwaysTrue;
653+
}
654+
break;
655+
656+
case GT_EQ:
657+
case GT_NE:
658+
if ((xLower.IsConstant() && yUpper.IsConstant() && (xLower.GetConstant() > yUpper.GetConstant())) ||
659+
(xUpper.IsConstant() && yLower.IsConstant() && (xUpper.GetConstant() < yLower.GetConstant())))
660+
{
661+
return relop == GT_EQ ? RelationKind::AlwaysFalse : RelationKind::AlwaysTrue;
662+
}
663+
break;
664+
665+
default:
666+
assert(!"unknown comparison operator");
667+
break;
668+
}
669+
return RelationKind::Unknown;
670+
}
588671
};
589672

590673
class RangeCheck

0 commit comments

Comments
 (0)