diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 4cd476105d3fa8..1e36f757cfbc2e 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -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)) { diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 333ee2480f128b..6c8e1e067584f2 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -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; @@ -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; diff --git a/src/coreclr/jit/rangecheck.h b/src/coreclr/jit/rangecheck.h index 19d59da6cc91ed..9db82510bc659f 100644 --- a/src/coreclr/jit/rangecheck.h +++ b/src/coreclr/jit/rangecheck.h @@ -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