Skip to content
Merged
73 changes: 34 additions & 39 deletions src/coreclr/jit/rangecheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,17 @@ struct Range
return lLimit.IsConstant() && uLimit.IsConstant() && IsValid();
}

// Check if the range represents a single constant value. Example: [7..7]
bool IsSingleConstValue(int* pConstVal) const
{
if (lLimit.IsConstant() && lLimit.Equals(uLimit))
{
*pConstVal = lLimit.GetConstant();
return true;
}
return false;
}

bool IsUndef() const
{
return lLimit.IsUndef() && uLimit.IsUndef();
Expand Down Expand Up @@ -393,61 +404,45 @@ struct RangeOps

static Range And(const Range& r1, const Range& r2)
{
const Limit& r1lo = r1.LowerLimit();
const Limit& r2lo = r2.LowerLimit();

const Limit& r1hi = r1.UpperLimit();
const Limit& r2hi = r2.UpperLimit();

Range result = Limit(Limit::keUnknown);
// Conservatively expect at least one operand to be a single-value constant.
// The math gets too complicated otherwise (and rarely useful in practice).
int r1ConstVal;
int r2ConstVal;
bool r1IsConstVal = r1.IsSingleConstValue(&r1ConstVal);
bool r2IsConstVal = r2.IsSingleConstValue(&r2ConstVal);

// if either lower bound is a non-negative constant, result >= 0
if ((r1lo.IsConstant() && (r1lo.GetConstant() >= 0)) || (r2lo.IsConstant() && (r2lo.GetConstant() >= 0)))
// Both ranges are single constant values.
// Example: [7..7] & [3..3] = [3..3]
if (r1IsConstVal && r2IsConstVal)
{
// Example: [4.. ] & [unknown.. ] = [4.. ]
result.lLimit = Limit(Limit::keConstant, 0);
return Range(Limit(Limit::keConstant, r1ConstVal & r2ConstVal));
}
// Even if both r1 and r2 lower bounds are known non-negative constants, still the best we can do
// is >= 0 in a general case due to the way bitwise AND works.

// if either upper bound is a non-negative constant, result <= cns
if (r2hi.IsConstant() && (r2hi.GetConstant() >= 0))
{
// Example: [ ..X] & [ ..5] = [ ..5]
result.uLimit = Limit(Limit::keConstant, r2hi.GetConstant());
}
else if (r1hi.IsConstant() && (r1hi.GetConstant() >= 0))
// One of the ranges is a single constant value.
// Example: [unknown..unknown] & [3..3] = [0..3]
if (r1IsConstVal && (r1ConstVal >= 0))
{
// Example: [ ..5] & [ ..X] = [ ..5]
result.uLimit = Limit(Limit::keConstant, r1hi.GetConstant());
return Range(Limit(Limit::keConstant, 0), Limit(Limit::keConstant, r1ConstVal));
}

// Both upper bounds are constant, take the min
if (r1hi.IsConstant() && r2hi.IsConstant() && (r1hi.GetConstant() >= 0) && (r2hi.GetConstant() >= 0))
if (r2IsConstVal && (r2ConstVal >= 0))
{
// Example: [ ..7] & [ ..5] = [ ..5]
result.uLimit = Limit(Limit::keConstant, min(r1hi.GetConstant(), r2hi.GetConstant()));
return Range(Limit(Limit::keConstant, 0), Limit(Limit::keConstant, r2ConstVal));
}

return result;
// Otherwise, we cannot determine the result range.
return Range(Limit(Limit::keUnknown));
}

static Range UnsignedMod(const Range& r1, const Range& r2)
{
Range result = Limit(Limit::keUnknown);

const Limit& r2lo = r2.LowerLimit();
const Limit& r2hi = r2.UpperLimit();

// For X UMOD Y we only handle the case when Y is a fixed non-negative constant.
// For X UMOD Y we only handle the case when Y is a fixed positive constant.
// Example: X % 5 -> [0..4]
//
if (r2lo.IsConstant() && r2lo.Equals(r2hi) && (r2lo.GetConstant() > 0))
int r2ConstVal;
if (r2.IsSingleConstValue(&r2ConstVal) && (r2ConstVal > 0))
{
result.lLimit = Limit(Limit::keConstant, 0);
result.uLimit = Limit(Limit::keConstant, r2lo.GetConstant() - 1);
return Range(Limit(Limit::keConstant, 0), Limit(Limit::keConstant, r2ConstVal - 1));
}
return result;
return Range(Limit(Limit::keUnknown));
}

// Given two ranges "r1" and "r2", do a Phi merge. If "monIncreasing" is true,
Expand Down
25 changes: 25 additions & 0 deletions src/tests/JIT/Regression/JitBlue/Runtime_123583/Runtime_123583.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// Generated by Fuzzlyn v3.3 on 2026-01-24 18:57:51
// Run on X86 Windows
// Seed: 1391773829286756241-vectort,vector128,vector256,x86aes,x86avx,x86avx2,x86avx512bw,x86avx512bwvl,x86avx512cd,x86avx512cdvl,x86avx512dq,x86avx512dqvl,x86avx512f,x86avx512fvl,x86bmi1,x86bmi2,x86fma,x86lzcnt,x86pclmulqdq,x86popcnt,x86sse,x86sse2,x86sse3,x86sse41,x86sse42,x86ssse3,x86x86base
// Reduced from 51.8 KiB to 0.2 KiB in 00:01:36
// Debug: Prints 0 line(s)
// Release: Prints 1 line(s)

using Xunit;

public class Runtime_123583
{
public static sbyte s_4 = 1;

[Fact]
public static void TestEntryPoint()
{
if (35009 > (1264240267 & (sbyte)(-s_4)))
{
throw new System.InvalidOperationException();
}
}
}
Loading