From 7c90bd1d085766b92ad47269a80857f8d8d5c1ba Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 19 Apr 2026 13:28:28 +0200 Subject: [PATCH 1/8] JIT: eliminate redundant span checks across loop back-edge phis Recognize that a back-edge phi-arg's VN equals the (checkedBound + cns) form referenced by an O2K_CHECKED_BOUND_ADD_CNS assertion in RangeCheck::MergeEdgeAssertions. Read phi-arg VNs from the SSA def's m_vnPair in optVisitReachingAssertions; this is always at least as precise as phiArg->gtVNPair (which fgValueNumberPhiDef may leave as NoVN or stale when it refuses to back-patch a loop-varying VN). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/jit/compiler.hpp | 13 +++++++++++-- src/coreclr/jit/rangecheck.cpp | 28 ++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 0ff1ab40625d97..1365cb44f6628b 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -5626,8 +5626,17 @@ Compiler::AssertVisit Compiler::optVisitReachingAssertions(ValueNum vn, TAssertV return AssertVisit::Abort; } - const ValueNum phiArgVN = vnStore->VNConservativeNormalValue(phiArg->gtVNPair); - ASSERT_TP assertions = optGetEdgeAssertions(ssaDef->GetBlock(), phiArg->gtPredBB); + // Read the VN from the phi arg's SSA def rather than from phiArg->gtVNPair: by SSA + // construction the value flowing along this predecessor edge is exactly that SSA + // def's value, and m_vnPair is always at least as precise as the cached gtVNPair. + // In particular, fgValueNumberPhiDef may leave gtVNPair as NoVN (or as a stale prior + // VN) when it refuses to back-patch a loop-varying SSA-def VN into the phi-arg slot + // -- a hygiene constraint for general gtVNPair consumers that does not apply here, + // since we hand the VN to the visitor together with this edge's assertion set and + // never write it back into any tree. + LclSsaVarDsc* phiArgSsaDef = lvaGetDesc(phiArg)->GetPerSsaData(phiArg->GetSsaNum()); + const ValueNum phiArgVN = vnStore->VNConservativeNormalValue(phiArgSsaDef->m_vnPair); + ASSERT_TP assertions = optGetEdgeAssertions(ssaDef->GetBlock(), phiArg->gtPredBB); if (argVisitor(phiArgVN, assertions) == AssertVisit::Abort) { // The visitor wants to abort the walk. diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index dca63fb6efdb2a..13b99f65574761 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -1082,17 +1082,33 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp, 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)) { + const ValueNum boundVN = curAssertion.GetOp2().GetCheckedBound(); + const int boundCns = curAssertion.GetOp2().GetCheckedBoundConstant(); + + // Check whether normalLclVN VN-equals the op2 expression "(boundVN + boundCns)": + // - trivially, when boundCns is 0 and normalLclVN == boundVN, or + // - when normalLclVN itself is the value-number "ADD(boundVN, boundCns)". + // + // The general case commonly arises on a loop back-edge where the induction variable + // is e.g. V = phi(V0, V - 8), and the back-edge JTRUE generates an assertion of the + // form "8 <= (V + -8)" against the phi's VN. We want to apply that assertion to the + // value of the back-edge phi-arg "(V - 8)" itself. + ValueNum addOp; + int addCns; + const bool normalLclVNMatchesOp2 = + ((normalLclVN == boundVN) && (boundCns == 0)) || + (comp->vnStore->IsVNBinFuncWithConst(normalLclVN, VNF_ADD, &addOp, &addCns) && (addOp == boundVN) && + (addCns == boundCns)); + if (canUseCheckedBounds && (normalLclVN == curAssertion.GetOp1().GetVN())) { cmpOper = Compiler::AssertionDsc::ToCompareOper(curAssertion.GetKind(), &isUnsigned); - limit = Limit(Limit::keBinOpArray, curAssertion.GetOp2().GetCheckedBound(), - curAssertion.GetOp2().GetCheckedBoundConstant()); + limit = Limit(Limit::keBinOpArray, boundVN, boundCns); } - else if ((normalLclVN == curAssertion.GetOp2().GetCheckedBound()) && - (curAssertion.GetOp2().GetCheckedBoundConstant() == 0)) + else if (normalLclVNMatchesOp2) { - // Check if it's "Const (checkedBndVN + 0)" or in other words "Const checkedBndVN" - // If normalLclVN == checkedBndVN, then we can deduce "normalLclVN Const" + // Check if it's "Const (checkedBndVN + cns)" or in other words "Const normalLclVN" + // If normalLclVN VN-equals (checkedBndVN + cns), we can deduce "normalLclVN Const" // // Example: We created "O1K_VN(vn1) - OAK_GT - O2K_CHECKED_BOUND_ADD_CNS(vn2, 0)" // if vn1 is a constant (say, 100) and vn2 is our normalLclVN, we can deduce "normalLclVN < 100" From ab1dc6be9bd04767285e70693a4168f07a74474e Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 19 Apr 2026 15:05:58 +0200 Subject: [PATCH 2/8] Address TP regression: gate the new fallbacks on cheap pre-checks - optVisitReachingAssertions: only fall back to the SSA def's m_vnPair when phiArg->gtVNPair is NoVN, restoring the cheap fast-path for the common case. - MergeEdgeAssertions: skip the IsVNBinFuncWithConst lookup when boundCns == 0, since VN normalizes ADD(x, 0) to x, so any positive match would already be caught by the trivial check. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/jit/compiler.hpp | 23 ++++++++++++----------- src/coreclr/jit/rangecheck.cpp | 8 +++++++- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 1365cb44f6628b..33e6f7badcf9ec 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -5626,17 +5626,18 @@ Compiler::AssertVisit Compiler::optVisitReachingAssertions(ValueNum vn, TAssertV return AssertVisit::Abort; } - // Read the VN from the phi arg's SSA def rather than from phiArg->gtVNPair: by SSA - // construction the value flowing along this predecessor edge is exactly that SSA - // def's value, and m_vnPair is always at least as precise as the cached gtVNPair. - // In particular, fgValueNumberPhiDef may leave gtVNPair as NoVN (or as a stale prior - // VN) when it refuses to back-patch a loop-varying SSA-def VN into the phi-arg slot - // -- a hygiene constraint for general gtVNPair consumers that does not apply here, - // since we hand the VN to the visitor together with this edge's assertion set and - // never write it back into any tree. - LclSsaVarDsc* phiArgSsaDef = lvaGetDesc(phiArg)->GetPerSsaData(phiArg->GetSsaNum()); - const ValueNum phiArgVN = vnStore->VNConservativeNormalValue(phiArgSsaDef->m_vnPair); - ASSERT_TP assertions = optGetEdgeAssertions(ssaDef->GetBlock(), phiArg->gtPredBB); + ValueNum phiArgVN = vnStore->VNConservativeNormalValue(phiArg->gtVNPair); + if (phiArgVN == ValueNumStore::NoVN) + { + // fgValueNumberPhiDef may leave gtVNPair as NoVN when it refuses to back-patch + // a loop-varying SSA-def VN into the phi-arg slot (a hygiene constraint for general + // gtVNPair consumers that does not apply here, since we hand the VN to the visitor + // together with this edge's assertion set and never write it back into any tree). + // Fall back to the SSA def's VN, which by SSA construction names the same value. + LclSsaVarDsc* phiArgSsaDef = lvaGetDesc(phiArg)->GetPerSsaData(phiArg->GetSsaNum()); + phiArgVN = vnStore->VNConservativeNormalValue(phiArgSsaDef->m_vnPair); + } + ASSERT_TP assertions = optGetEdgeAssertions(ssaDef->GetBlock(), phiArg->gtPredBB); if (argVisitor(phiArgVN, assertions) == AssertVisit::Abort) { // The visitor wants to abort the walk. diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 13b99f65574761..35294c98d9c2e0 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -1093,11 +1093,17 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp, // is e.g. V = phi(V0, V - 8), and the back-edge JTRUE generates an assertion of the // form "8 <= (V + -8)" against the phi's VN. We want to apply that assertion to the // value of the back-edge phi-arg "(V - 8)" itself. + // + // Note: when boundCns == 0, the trivial check above is exhaustive -- VN normalizes + // ADD(x, 0) to x, so any IsVNBinFuncWithConst match would also have to satisfy + // normalLclVN == boundVN, which the trivial check already covers. Skip the lookup + // in that case to keep this fast-path cheap. ValueNum addOp; int addCns; const bool normalLclVNMatchesOp2 = ((normalLclVN == boundVN) && (boundCns == 0)) || - (comp->vnStore->IsVNBinFuncWithConst(normalLclVN, VNF_ADD, &addOp, &addCns) && (addOp == boundVN) && + ((boundCns != 0) && + comp->vnStore->IsVNBinFuncWithConst(normalLclVN, VNF_ADD, &addOp, &addCns) && (addOp == boundVN) && (addCns == boundCns)); if (canUseCheckedBounds && (normalLclVN == curAssertion.GetOp1().GetVN())) From ff00e93d3f4ea6fdf2142f52f02d29898426ced0 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 19 Apr 2026 16:43:13 +0200 Subject: [PATCH 3/8] fix TP --- src/coreclr/jit/rangecheck.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 35294c98d9c2e0..34009e453cb91d 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -848,7 +848,7 @@ Range RangeCheck::GetRangeFromAssertions(Compiler* comp, ValueNum num, ASSERT_VA Range phiRange = Range(Limit(Limit::keUndef)); auto visitor = [comp, &phiRange, &budget](ValueNum reachingVN, ASSERT_TP reachingAssertions) { // call GetRangeFromAssertions for each reaching VN using reachingAssertions - Range edgeRange = GetRangeFromAssertions(comp, reachingVN, reachingAssertions, --budget); + Range edgeRange = GetRangeFromAssertions(comp, reachingVN, reachingAssertions, min(3, --budget)); // If phiRange is not yet set, set it to the first edgeRange // else merge it with the new edgeRange. Example: [10..100] U [50..150] = [10..150] @@ -1102,9 +1102,8 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp, int addCns; const bool normalLclVNMatchesOp2 = ((normalLclVN == boundVN) && (boundCns == 0)) || - ((boundCns != 0) && - comp->vnStore->IsVNBinFuncWithConst(normalLclVN, VNF_ADD, &addOp, &addCns) && (addOp == boundVN) && - (addCns == boundCns)); + ((boundCns != 0) && comp->vnStore->IsVNBinFuncWithConst(normalLclVN, VNF_ADD, &addOp, &addCns) && + (addOp == boundVN) && (addCns == boundCns)); if (canUseCheckedBounds && (normalLclVN == curAssertion.GetOp1().GetVN())) { From 139853f852cb33f2c655315a783c885ffc79e91f Mon Sep 17 00:00:00 2001 From: EgorBo Date: Wed, 22 Apr 2026 18:18:26 +0200 Subject: [PATCH 4/8] fb --- src/coreclr/jit/rangecheck.cpp | 48 +++++++++++++++++++++++++++------- src/coreclr/jit/rangecheck.h | 7 +++++ 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 34009e453cb91d..65091b74f669c8 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -650,11 +650,34 @@ void RangeCheck::MergeEdgeAssertions(GenTreeLclVarCommon* lcl, ASSERT_VALARG_TP // comp - the compiler instance // num - the value number to analyze range for // assertions - the assertions to use +// budget - the remaining budget for recursive analysis // // Return Value: // The computed range // Range RangeCheck::GetRangeFromAssertions(Compiler* comp, ValueNum num, ASSERT_VALARG_TP assertions, int budget) +{ + ValueNumStore::SmallValueNumSet set; + return GetRangeFromAssertionsWorker(comp, num, assertions, budget, &set); +} + +//------------------------------------------------------------------------ +// GetRangeFromAssertions: Cheaper version of TryGetRange that is based purely on assertions +// and does not require a full range analysis based on SSA. +// +// Arguments: +// comp - the compiler instance +// num - the value number to analyze range for +// assertions - the assertions to use +// budget - the remaining budget for recursive analysis +// visited - the set of value numbers already visited in the current search +// path to prevent infinite recursion +// +// Return Value: +// The computed range +// +Range RangeCheck::GetRangeFromAssertionsWorker( + Compiler* comp, ValueNum num, ASSERT_VALARG_TP assertions, int budget, ValueNumStore::SmallValueNumSet* visited) { // Start with the widest possible constant range. Range result = Range(Limit(Limit::keConstant, INT32_MIN), Limit(Limit::keConstant, INT32_MAX)); @@ -664,6 +687,12 @@ Range RangeCheck::GetRangeFromAssertions(Compiler* comp, ValueNum num, ASSERT_VA return result; } + if (visited->Add(comp, num)) + { + // We've come back to a node we've already visited + return result; + } + // Currently, we only handle int32 and smaller integer types. assert(genTypeSize(comp->vnStore->TypeOfVN(num)) <= 4); @@ -699,7 +728,8 @@ Range RangeCheck::GetRangeFromAssertions(Compiler* comp, ValueNum num, ASSERT_VA // if its range is within the castTo range, we can use that (and the cast is basically a no-op). if (comp->vnStore->TypeOfVN(funcApp.m_args[0]) == TYP_INT) { - Range castOpRange = GetRangeFromAssertions(comp, funcApp.m_args[0], assertions, --budget); + Range castOpRange = + GetRangeFromAssertionsWorker(comp, funcApp.m_args[0], assertions, --budget, visited); if (castOpRange.IsConstantRange() && (castOpRange.LowerLimit().GetConstant() >= castToTypeRange.LowerLimit().GetConstant()) && (castOpRange.UpperLimit().GetConstant() <= castToTypeRange.UpperLimit().GetConstant())) @@ -713,7 +743,7 @@ Range RangeCheck::GetRangeFromAssertions(Compiler* comp, ValueNum num, ASSERT_VA case VNF_NEG: { - Range r1 = GetRangeFromAssertions(comp, funcApp.m_args[0], assertions, --budget); + Range r1 = GetRangeFromAssertionsWorker(comp, funcApp.m_args[0], assertions, --budget, visited); Range unaryOpResult = RangeOps::Negate(r1); // We can use the result only if it never overflows. @@ -732,8 +762,8 @@ Range RangeCheck::GetRangeFromAssertions(Compiler* comp, ValueNum num, ASSERT_VA case VNF_UMOD: { // Get ranges of both operands and perform the same operation on the ranges. - Range r1 = GetRangeFromAssertions(comp, funcApp.m_args[0], assertions, --budget); - Range r2 = GetRangeFromAssertions(comp, funcApp.m_args[1], assertions, --budget); + Range r1 = GetRangeFromAssertionsWorker(comp, funcApp.m_args[0], assertions, --budget, visited); + Range r2 = GetRangeFromAssertionsWorker(comp, funcApp.m_args[1], assertions, --budget, visited); Range binOpResult = Range(Limit(Limit::keUnknown)); switch (funcApp.m_func) { @@ -799,8 +829,8 @@ Range RangeCheck::GetRangeFromAssertions(Compiler* comp, ValueNum num, ASSERT_VA if ((genActualType(comp->vnStore->TypeOfVN(funcApp.m_args[0])) == TYP_INT) && (genActualType(comp->vnStore->TypeOfVN(funcApp.m_args[1])) == TYP_INT)) { - Range r1 = GetRangeFromAssertions(comp, funcApp.m_args[0], assertions, --budget); - Range r2 = GetRangeFromAssertions(comp, funcApp.m_args[1], assertions, --budget); + Range r1 = GetRangeFromAssertionsWorker(comp, funcApp.m_args[0], assertions, --budget, visited); + Range r2 = GetRangeFromAssertionsWorker(comp, funcApp.m_args[1], assertions, --budget, visited); bool isUnsigned = true; genTreeOps cmpOper; @@ -846,9 +876,9 @@ Range RangeCheck::GetRangeFromAssertions(Compiler* comp, ValueNum num, ASSERT_VA } Range phiRange = Range(Limit(Limit::keUndef)); - auto visitor = [comp, &phiRange, &budget](ValueNum reachingVN, ASSERT_TP reachingAssertions) { - // call GetRangeFromAssertions for each reaching VN using reachingAssertions - Range edgeRange = GetRangeFromAssertions(comp, reachingVN, reachingAssertions, min(3, --budget)); + auto visitor = [comp, &phiRange, &budget, visited](ValueNum reachingVN, ASSERT_TP reachingAssertions) { + // call GetRangeFromAssertionsWorker for each reaching VN using reachingAssertions + Range edgeRange = GetRangeFromAssertionsWorker(comp, reachingVN, reachingAssertions, --budget, visited); // If phiRange is not yet set, set it to the first edgeRange // else merge it with the new edgeRange. Example: [10..100] U [50..150] = [10..150] diff --git a/src/coreclr/jit/rangecheck.h b/src/coreclr/jit/rangecheck.h index 0d005e7acfcc23..cab98b29c85b60 100644 --- a/src/coreclr/jit/rangecheck.h +++ b/src/coreclr/jit/rangecheck.h @@ -776,6 +776,13 @@ class RangeCheck typedef JitHashTable, Range*> RangeMap; typedef JitHashTable, BasicBlock*> SearchPath; + // Cheaper version of TryGetRange that is based only on incoming assertions. + static Range GetRangeFromAssertionsWorker(Compiler* comp, + ValueNum num, + ASSERT_VALARG_TP assertions, + int budget, + ValueNumStore::SmallValueNumSet* visited); + int GetArrLength(ValueNum vn); // Check whether the computed range is within 0 and upper bounds. This function From 22f75a2b4b7ad77f542bd75f6e4dfd232800389e Mon Sep 17 00:00:00 2001 From: EgorBo Date: Wed, 22 Apr 2026 18:20:48 +0200 Subject: [PATCH 5/8] oops --- src/coreclr/jit/compiler.hpp | 19 +++++++------------ src/coreclr/jit/rangecheck.cpp | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 33e6f7badcf9ec..0472160d20f3d2 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -5626,18 +5626,13 @@ Compiler::AssertVisit Compiler::optVisitReachingAssertions(ValueNum vn, TAssertV return AssertVisit::Abort; } - ValueNum phiArgVN = vnStore->VNConservativeNormalValue(phiArg->gtVNPair); - if (phiArgVN == ValueNumStore::NoVN) - { - // fgValueNumberPhiDef may leave gtVNPair as NoVN when it refuses to back-patch - // a loop-varying SSA-def VN into the phi-arg slot (a hygiene constraint for general - // gtVNPair consumers that does not apply here, since we hand the VN to the visitor - // together with this edge's assertion set and never write it back into any tree). - // Fall back to the SSA def's VN, which by SSA construction names the same value. - LclSsaVarDsc* phiArgSsaDef = lvaGetDesc(phiArg)->GetPerSsaData(phiArg->GetSsaNum()); - phiArgVN = vnStore->VNConservativeNormalValue(phiArgSsaDef->m_vnPair); - } - ASSERT_TP assertions = optGetEdgeAssertions(ssaDef->GetBlock(), phiArg->gtPredBB); + // fgValueNumberPhiDef may leave gtVNPair as NoVN when it refuses to back-patch + // a loop-varying SSA-def VN into the phi-arg slot (a hygiene constraint for general + // gtVNPair consumers that does not apply here, since we hand the VN to the visitor + // together with this edge's assertion set and never write it back into any tree). + LclSsaVarDsc* phiArgSsaDef = lvaGetDesc(phiArg)->GetPerSsaData(phiArg->GetSsaNum()); + ValueNum phiArgVN = vnStore->VNConservativeNormalValue(phiArgSsaDef->m_vnPair); + ASSERT_TP assertions = optGetEdgeAssertions(ssaDef->GetBlock(), phiArg->gtPredBB); if (argVisitor(phiArgVN, assertions) == AssertVisit::Abort) { // The visitor wants to abort the walk. diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 65091b74f669c8..e961a6e6d001e9 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -687,7 +687,7 @@ Range RangeCheck::GetRangeFromAssertionsWorker( return result; } - if (visited->Add(comp, num)) + if (!visited->Add(comp, num)) { // We've come back to a node we've already visited return result; From a9e6a0e07e43f2625bbe3cc57b029623229a9170 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Wed, 22 Apr 2026 19:58:15 +0200 Subject: [PATCH 6/8] feedback --- src/coreclr/jit/rangecheck.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index e961a6e6d001e9..5b3c93c6fb5804 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -687,12 +687,6 @@ Range RangeCheck::GetRangeFromAssertionsWorker( return result; } - if (!visited->Add(comp, num)) - { - // We've come back to a node we've already visited - return result; - } - // Currently, we only handle int32 and smaller integer types. assert(genTypeSize(comp->vnStore->TypeOfVN(num)) <= 4); @@ -894,10 +888,20 @@ Range RangeCheck::GetRangeFromAssertionsWorker( return Compiler::AssertVisit::Abort; }; - if (comp->optVisitReachingAssertions(num, visitor) == Compiler::AssertVisit::Continue && !phiRange.IsUndef()) + // If it's a phi, we need to look at the reaching assertions for each of the phi args and merge them together. + if (comp->vnStore->IsPhiDef(num)) { - assert(phiRange.IsConstantRange()); - result = phiRange; + if (!visited->Add(comp, num)) + { + // We already visited this phi in the current search path, which means we have a cycle. + return result; + } + + if ((comp->optVisitReachingAssertions(num, visitor) == Compiler::AssertVisit::Continue) && !phiRange.IsUndef()) + { + assert(phiRange.IsConstantRange()); + result = phiRange; + } } MergeEdgeAssertions(comp, num, ValueNumStore::NoVN, assertions, &result, false); From 624a6f0ea9d5f3e154627c313a2758b96dc4a06d Mon Sep 17 00:00:00 2001 From: EgorBo Date: Wed, 22 Apr 2026 22:58:18 +0200 Subject: [PATCH 7/8] improve diffs --- src/coreclr/jit/rangecheck.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 5b3c93c6fb5804..13fc60206778c5 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -889,19 +889,11 @@ Range RangeCheck::GetRangeFromAssertionsWorker( }; // If it's a phi, we need to look at the reaching assertions for each of the phi args and merge them together. - if (comp->vnStore->IsPhiDef(num)) + if (comp->vnStore->IsPhiDef(num) && visited->Add(comp, num) && + (comp->optVisitReachingAssertions(num, visitor) == Compiler::AssertVisit::Continue) && !phiRange.IsUndef()) { - if (!visited->Add(comp, num)) - { - // We already visited this phi in the current search path, which means we have a cycle. - return result; - } - - if ((comp->optVisitReachingAssertions(num, visitor) == Compiler::AssertVisit::Continue) && !phiRange.IsUndef()) - { - assert(phiRange.IsConstantRange()); - result = phiRange; - } + assert(phiRange.IsConstantRange()); + result = phiRange; } MergeEdgeAssertions(comp, num, ValueNumStore::NoVN, assertions, &result, false); From fff282b14a3f4b3a309188f3205e67e419b4bb1f Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Wed, 22 Apr 2026 23:05:06 +0200 Subject: [PATCH 8/8] Update src/coreclr/jit/rangecheck.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- 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 13fc60206778c5..a8f9ecd4e4a3fd 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -662,7 +662,7 @@ Range RangeCheck::GetRangeFromAssertions(Compiler* comp, ValueNum num, ASSERT_VA } //------------------------------------------------------------------------ -// GetRangeFromAssertions: Cheaper version of TryGetRange that is based purely on assertions +// GetRangeFromAssertionsWorker: Cheaper version of TryGetRange that is based purely on assertions // and does not require a full range analysis based on SSA. // // Arguments: