From 2cbeb22ea19ba913fa1f5d26e0824492f314bd15 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Fri, 9 Oct 2026 02:12:08 +0200 Subject: [PATCH 1/2] JIT: preserve relocatable handles when simplifying ordered comparisons PR description draft: Reject signed relational constant folding involving relocatable handles, just as for unsigned comparisons and arithmetic. A compile-time handle placeholder does not establish the ordering of its relocated runtime address. Also prevent long-to-int VN narrowing from converting handle VNs into ordinary numeric constants and bypassing that rejection. Retain the existing identity-based equality/null folding. Add a single-file regression comparing a runtime type handle against a numeric bound for all signed relational operators, reversed operands, and unsigned controls. Fixes https://github.com/dotnet/runtime/issues/134891 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ae8155d6-f240-4542-a1da-c721ba933c91 --- src/coreclr/jit/valuenum.cpp | 12 ++-- .../JIT/Regression_ro_2/Runtime_134891.cs | 60 +++++++++++++++++++ 2 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 src/tests/JIT/Regression_ro_2/Runtime_134891.cs diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 6bed99af78a9b5..90d26202e7a8a2 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -2099,9 +2099,9 @@ ValueNum ValueNumStore::VNIgnoreIntToLongCast(ValueNum vn) } } - // Also look through any long-typed integral constant that fits in an int. + // Also look through any non-handle long-typed integral constant that fits in an int. int intCns; - if (IsVNIntegralConstant(vn, &intCns)) + if (!IsVNHandle(vn) && IsVNIntegralConstant(vn, &intCns)) { return VNForIntCon(intCns); } @@ -4936,6 +4936,10 @@ bool ValueNumStore::VNEvalCanFoldBinaryFunc(var_types type, VNFunc func, ValueNu case GT_RSZ: case GT_ROL: case GT_ROR: + case GT_GT: + case GT_GE: + case GT_LT: + case GT_LE: if (m_compiler->opts.compReloc && (IsVNHandle(arg0VN) || IsVNHandle(arg1VN))) { return false; @@ -4944,10 +4948,6 @@ bool ValueNumStore::VNEvalCanFoldBinaryFunc(var_types type, VNFunc func, ValueNu case GT_EQ: case GT_NE: - case GT_GT: - case GT_GE: - case GT_LT: - case GT_LE: break; default: diff --git a/src/tests/JIT/Regression_ro_2/Runtime_134891.cs b/src/tests/JIT/Regression_ro_2/Runtime_134891.cs new file mode 100644 index 00000000000000..7ced64bdb901d7 --- /dev/null +++ b/src/tests/JIT/Regression_ro_2/Runtime_134891.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +public class Runtime_134891 +{ + private const int Bound = 0x1000000; + + [Fact] + public static void TestEntryPoint() + { + nint handle = GetHandle(); + Assert.Equal(handle > Bound, Greater()); + Assert.Equal(handle >= Bound, GreaterOrEqual()); + Assert.Equal(handle < Bound, Less()); + Assert.Equal(handle <= Bound, LessOrEqual()); + Assert.Equal(Bound > handle, GreaterReversed()); + Assert.Equal(Bound >= handle, GreaterOrEqualReversed()); + Assert.Equal(Bound < handle, LessReversed()); + Assert.Equal(Bound <= handle, LessOrEqualReversed()); + Assert.Equal((nuint)handle > Bound, GreaterUnsigned()); + Assert.Equal((nuint)handle < Bound, LessUnsigned()); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static nint GetHandle() => typeof(Runtime_134891).TypeHandle.Value; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool Greater() => typeof(Runtime_134891).TypeHandle.Value > Bound; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool GreaterOrEqual() => typeof(Runtime_134891).TypeHandle.Value >= Bound; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool Less() => typeof(Runtime_134891).TypeHandle.Value < Bound; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool LessOrEqual() => typeof(Runtime_134891).TypeHandle.Value <= Bound; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool GreaterReversed() => Bound > typeof(Runtime_134891).TypeHandle.Value; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool GreaterOrEqualReversed() => Bound >= typeof(Runtime_134891).TypeHandle.Value; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool LessReversed() => Bound < typeof(Runtime_134891).TypeHandle.Value; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool LessOrEqualReversed() => Bound <= typeof(Runtime_134891).TypeHandle.Value; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool GreaterUnsigned() => (nuint)typeof(Runtime_134891).TypeHandle.Value > Bound; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool LessUnsigned() => (nuint)typeof(Runtime_134891).TypeHandle.Value < Bound; +} From 7703700520bb3ebd249c29d6af7b6277b00bdd15 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Fri, 9 Oct 2026 14:32:38 +0200 Subject: [PATCH 2/2] JIT: clarify non-handle constant narrowing documentation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 58815d76-2d1f-4ad8-9153-cca0f87b58e5 --- src/coreclr/jit/valuenum.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 90d26202e7a8a2..fd1863b62debee 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -2069,7 +2069,7 @@ ValueNum ValueNumStore::VNForCastOper(var_types castToType, bool srcIsUnsigned) //------------------------------------------------------------------------ // VNIgnoreIntToLongCast: Looks through a sign-extending int-to-long cast -// or convert long-typed integral constants to int. +// or converts non-handle long-typed integral constants to int. // // Arguments: // vn - The value number to inspect. @@ -2077,7 +2077,7 @@ ValueNum ValueNumStore::VNForCastOper(var_types castToType, bool srcIsUnsigned) // Return Value: // The value number of the original TYP_INT operand if 'vn' is a VNF_Cast // that sign-extends a TYP_INT to TYP_LONG; or the value number of a TYP_INT -// constant if 'vn' is a TYP_LONG constant that fits in an int; otherwise, 'vn' itself. +// constant if 'vn' is a non-handle TYP_LONG constant that fits in an int; otherwise, 'vn' itself. // ValueNum ValueNumStore::VNIgnoreIntToLongCast(ValueNum vn) {