From 8a48875e0fe24aedca0d3ce3e950d24b00d76a58 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 5 Mar 2026 20:49:36 +0100 Subject: [PATCH 1/3] Fix bounds checks in #123085 repro --- src/coreclr/jit/rangecheck.cpp | 40 ++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index d7426258011e25..01523ab8afb3d7 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -1017,14 +1017,26 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp, ValueNum addOpVN; int addOpCns; if (comp->vnStore->IsVNBinFuncWithConst(curAssertion.GetOp1().GetVN(), VNF_ADD, &addOpVN, &addOpCns) && - (addOpVN == normalLclVN) && (addOpCns >= 0)) + (addOpVN == normalLclVN)) { - cmpOper = GT_LT; - limit = Limit(Limit::keBinOpArray, preferredBoundVN, -addOpCns); - isUnsigned = false; - // The comparison being unsigned may also hint that the lower bound is -CNS1, but it's - // unlikely to be useful, so we ignore it for now. The whole thing will work only if some other - // assertion proves that the normalLclVN's lower bound is non-negative. + if (addOpCns >= 0) + { + cmpOper = GT_LT; + limit = Limit(Limit::keBinOpArray, preferredBoundVN, -addOpCns); + isUnsigned = false; + } + else if (addOpCns > INT32_MIN) + { + // (normalLclVN + negConst) u< bound, with bound non-negative. + // Since the comparison is unsigned, (normalLclVN + negConst) must not have wrapped, + // which means normalLclVN >= -negConst. + cmpOper = GT_GE; + limit = Limit(Limit::keConstant, -addOpCns); + } + else + { + continue; + } } else { @@ -2013,8 +2025,18 @@ Range RangeCheck::ComputeRange(BasicBlock* block, GenTree* expr, bool monIncreas } else if (expr->OperIs(GT_ARR_LENGTH)) { - // Better than keUnknown - range = Range(Limit(Limit::keConstant, 0), Limit(Limit::keConstant, CORINFO_Array_MaxLength)); + ValueNum arrLenVN = m_compiler->vnStore->VNConservativeNormalValue(expr->gtVNPair); + if (arrLenVN == m_preferredBound) + { + // If the ARR_LENGTH VN matches the bounds check's length VN, represent it symbolically + // so the PHI merge can combine it with other symbolic ranges referencing the same bound. + range = Range(Limit(Limit::keConstant, 0), Limit(Limit::keBinOpArray, arrLenVN, 0)); + } + else + { + // Better than keUnknown + range = Range(Limit(Limit::keConstant, 0), Limit(Limit::keConstant, CORINFO_Array_MaxLength)); + } } else { From 334ff9e5d07bee084f8a0bb39ab585a07561ed3f Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 5 Mar 2026 20:52:31 +0100 Subject: [PATCH 2/3] cleanup --- src/coreclr/jit/rangecheck.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 01523ab8afb3d7..9b9a89e1f5e2f9 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -2025,8 +2025,8 @@ Range RangeCheck::ComputeRange(BasicBlock* block, GenTree* expr, bool monIncreas } else if (expr->OperIs(GT_ARR_LENGTH)) { - ValueNum arrLenVN = m_compiler->vnStore->VNConservativeNormalValue(expr->gtVNPair); - if (arrLenVN == m_preferredBound) + ValueNum arrLenVN = m_compiler->optConservativeNormalVN(expr); + if ((arrLenVN != ValueNumStore::NoVN) && (arrLenVN == m_preferredBound)) { // If the ARR_LENGTH VN matches the bounds check's length VN, represent it symbolically // so the PHI merge can combine it with other symbolic ranges referencing the same bound. From 37174ab172021773674fb979a8d15d13fa4205f6 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 5 Mar 2026 23:44:47 +0100 Subject: [PATCH 3/3] feedback --- src/coreclr/jit/rangecheck.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 9b9a89e1f5e2f9..dca63fb6efdb2a 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -1030,8 +1030,9 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp, // (normalLclVN + negConst) u< bound, with bound non-negative. // Since the comparison is unsigned, (normalLclVN + negConst) must not have wrapped, // which means normalLclVN >= -negConst. - cmpOper = GT_GE; - limit = Limit(Limit::keConstant, -addOpCns); + cmpOper = GT_GE; + limit = Limit(Limit::keConstant, -addOpCns); + isUnsigned = false; } else {