From 1eb00ef3bfa93500a5ce26ab0017ffd540ef88e0 Mon Sep 17 00:00:00 2001 From: egorbot Date: Tue, 3 Mar 2026 00:33:15 +0100 Subject: [PATCH 1/3] "(uint)normalLclVN < span.Length" means normalLclVN's range is [0..INT32_MAX-1] --- src/coreclr/jit/rangecheck.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 4b372fa3b41017..fc90489276abac 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -1025,6 +1025,27 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp, continue; } } + // If we're not allowed to emit keBinOpArray, we still can deduce something useful from + // O2K_CHECKED_BOUND_ADD_CNS for never negative checked bounds + // + // Example: "(uint)normalLclVN < span.Length" means normalLclVN's range is [0..INT32_MAX-1] + // Example: "(uint)normalLclVN <= array.Length" means normalLclVN's range is [0..Array.MaxLength] + // + else if (!canUseCheckedBounds && curAssertion.IsRelop() && (curAssertion.GetOp1().GetVN() == normalLclVN) && + (curAssertion.GetOp2().KindIs(Compiler::O2K_CHECKED_BOUND_ADD_CNS)) && + (curAssertion.GetOp2().IsCheckedBoundNeverNegative()) && + (curAssertion.GetOp2().GetCheckedBoundConstant() == 0)) + { + // We can assume that the checked bound is within [0..INT32_MAX] thanks to IsCheckedBoundNeverNegative, + // but let's see if we can do better: we could call GetRangeFromAssertions on the checked bound's VN, + // but that may lead to infinite recursion. Also, the checked bound is usually either arr.Length or + // span.Length anyway. + ValueNum checkedBoundVN = curAssertion.GetOp2().GetCheckedBound(); + + int maxValue = comp->vnStore->IsVNArrLen(checkedBoundVN) ? CORINFO_Array_MaxLength : INT32_MAX; + cmpOper = Compiler::AssertionDsc::ToCompareOper(curAssertion.GetKind(), &isUnsigned); + limit = Limit(Limit::keConstant, maxValue); + } // Current assertion is of the form "i (checkedBndVN + cns)" else if (curAssertion.KindIs(Compiler::OAK_GE, Compiler::OAK_GT, Compiler::OAK_LE, Compiler::OAK_LT) && curAssertion.GetOp2().KindIs(Compiler::O2K_CHECKED_BOUND_ADD_CNS)) From a2152b1b0e5b3c1b51b0fb73797cb8aa8d21d603 Mon Sep 17 00:00:00 2001 From: egorbot Date: Tue, 3 Mar 2026 14:59:04 +0100 Subject: [PATCH 2/3] fix a few regressions --- src/coreclr/jit/rangecheck.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index fc90489276abac..32336222d6a139 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -1042,9 +1042,22 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp, // span.Length anyway. ValueNum checkedBoundVN = curAssertion.GetOp2().GetCheckedBound(); - int maxValue = comp->vnStore->IsVNArrLen(checkedBoundVN) ? CORINFO_Array_MaxLength : INT32_MAX; - cmpOper = Compiler::AssertionDsc::ToCompareOper(curAssertion.GetKind(), &isUnsigned); - limit = Limit(Limit::keConstant, maxValue); + int maxValue = INT32_MAX; + if (comp->vnStore->IsVNArrLen(checkedBoundVN)) + { + maxValue = CORINFO_Array_MaxLength; + } + else + { + int cns; + if (comp->vnStore->IsVNIntegralConstant(checkedBoundVN, &cns) && (cns >= 0) && (cns < maxValue)) + { + maxValue = cns; + } + } + + cmpOper = Compiler::AssertionDsc::ToCompareOper(curAssertion.GetKind(), &isUnsigned); + limit = Limit(Limit::keConstant, maxValue); } // Current assertion is of the form "i (checkedBndVN + cns)" else if (curAssertion.KindIs(Compiler::OAK_GE, Compiler::OAK_GT, Compiler::OAK_LE, Compiler::OAK_LT) && From 4073e242138c44f00ea920191f7d12b1b8436956 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Tue, 3 Mar 2026 15:00:20 +0100 Subject: [PATCH 3/3] Update rangecheck.cpp --- src/coreclr/jit/rangecheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 32336222d6a139..ca71ab895c42c5 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -1050,7 +1050,7 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp, else { int cns; - if (comp->vnStore->IsVNIntegralConstant(checkedBoundVN, &cns) && (cns >= 0) && (cns < maxValue)) + if (comp->vnStore->IsVNIntegralConstant(checkedBoundVN, &cns) && (cns >= 0)) { maxValue = cns; }