From eec842f9cb9b68f2ba6228bf8fb094e3e2cf0606 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Thu, 15 Oct 2026 03:51:23 +0200 Subject: [PATCH] JIT: sign-extend folded 32-bit RotateLeft/RotateRight constants impRotateHelper folds 32-bit rotates of constants into a uint32_t and passed it to gtNewIconNode, which zero-extended it on 64-bit hosts. TYP_INT constants must be sign-extended, so a result with bit 31 set (e.g. int.RotateLeft(1, 31)) was treated as +2^31 and comparisons against it were folded incorrectly. Cast the result to int32_t. Fixes #135814 Fixes #129099 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6ec7c151-8b7f-4bf9-8b1a-8c601a2965ec --- src/coreclr/jit/importercalls.cpp | 2 +- .../JIT/Regression_ro_2/Runtime_135814.cs | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/tests/JIT/Regression_ro_2/Runtime_135814.cs diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 85713ea11fcb76..368819875c69c5 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -6693,7 +6693,7 @@ GenTree* Compiler::impRotateHelper(var_types baseType, genTreeOps rotateOper) uint32_t cns1 = static_cast(op1->AsIntConCommon()->IconValue()); uint32_t res = (rotateOper == GT_ROL) ? BitOperations::RotateLeft(cns1, cns2) : BitOperations::RotateRight(cns1, cns2); - return gtNewIconNode(res, baseType); + return gtNewIconNode(static_cast(res), baseType); } } diff --git a/src/tests/JIT/Regression_ro_2/Runtime_135814.cs b/src/tests/JIT/Regression_ro_2/Runtime_135814.cs new file mode 100644 index 00000000000000..7afcb62501cd04 --- /dev/null +++ b/src/tests/JIT/Regression_ro_2/Runtime_135814.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Numerics; +using System.Runtime.CompilerServices; +using Xunit; + +public class Runtime_135814 +{ + [Fact] + public static void TestEntryPoint() + { + Assert.True(TestGt(3)); + Assert.False(TestLt(3)); + Assert.True(TestRor(3)); + Assert.Equal(-1, TestUInt()); + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + private static bool TestGt(int y) => y > int.RotateLeft(1, 31); + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + private static bool TestLt(int y) => y < int.RotateLeft(1, 31); + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + private static bool TestRor(int y) => y > int.RotateRight(1, 1); + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + private static int TestUInt() => (int)BitOperations.RotateRight(0xFFFFFFFFu, 3); +}