diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 3af72048706405..956480270d2e40 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -9059,12 +9059,18 @@ GenTreeQmark* Compiler::gtNewQmarkNode(var_types type, GenTree* cond, GenTreeCol GenTreeIntCon* Compiler::gtNewIconNode(ssize_t value, var_types type) { assert(genActualType(type) == type); + // For TYP_INT-sized constants the value must fit in int32_t; otherwise the + // node is in an invalid state and downstream SetIconValue / BashToConst + // will assert when the constant is updated. Wider values should use + // gtNewLconNode (TYP_LONG) or TYP_I_IMPL on 64-bit targets. + assert(genTypeSize(type) > genTypeSize(TYP_INT) || FitsIn(value)); return new (this, GT_CNS_INT) GenTreeIntCon(type, value); } GenTreeIntCon* Compiler::gtNewIconNodeWithVN(Compiler* comp, ssize_t value, var_types type) { assert(genActualType(type) == type); + assert(genTypeSize(type) > genTypeSize(TYP_INT) || FitsIn(value)); GenTreeIntCon* cns = new (this, GT_CNS_INT) GenTreeIntCon(type, value); comp->fgUpdateConstTreeValueNumber(cns); return cns; @@ -25492,7 +25498,7 @@ GenTree* Compiler::gtNewSimdIsNegativeInfinityNode(var_types type, if (simdBaseType == TYP_FLOAT) { simdBaseType = TYP_UINT; - cnsNode = gtNewIconNode(0xFF800000); + cnsNode = gtNewIconNode(static_cast(0xFF800000)); } else { diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 851e08288b3d35..1a8c83a0c6905e 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -6408,7 +6408,13 @@ GenTree* Compiler::impPrimitiveNamedIntrinsic(NamedIntrinsic intrinsic, else { uint32_t cns1 = static_cast(op1->AsIntConCommon()->IconValue()); - result = gtNewIconNode(BitOperations::RotateLeft(cns1, cns2), baseType); + // Sign-extend the unsigned fold result to int32_t so that downstream + // SetIconValue / BashToConst calls (which assert FitsIn for + // TYP_INT-sized constants) don't trip on the high-bit-set case + // (e.g. RotateLeft(0xFFFFFFFFu, k) -> 0xFFFFFFFF zero-extended to a + // positive ssize_t that doesn't fit in int32_t). + result = gtNewIconNode( + static_cast(BitOperations::RotateLeft(cns1, cns2)), baseType); } break; } @@ -6457,7 +6463,13 @@ GenTree* Compiler::impPrimitiveNamedIntrinsic(NamedIntrinsic intrinsic, else { uint32_t cns1 = static_cast(op1->AsIntConCommon()->IconValue()); - result = gtNewIconNode(BitOperations::RotateRight(cns1, cns2), baseType); + // Sign-extend the unsigned fold result to int32_t so that downstream + // SetIconValue / BashToConst calls (which assert FitsIn for + // TYP_INT-sized constants) don't trip on the high-bit-set case + // (e.g. RotateRight(0xFFFFFFFFu, k) -> 0xFFFFFFFF zero-extended to a + // positive ssize_t that doesn't fit in int32_t). + result = gtNewIconNode( + static_cast(BitOperations::RotateRight(cns1, cns2)), baseType); } break; } diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index b122b8de775259..989f28cbb7ede5 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -8350,7 +8350,16 @@ bool Lowering::TryLowerConstIntUDivOrUMod(GenTreeOp* divMod) if (!isDiv) { // divisor UMOD dividend = dividend SUB (div MUL divisor) - GenTree* divisor = m_compiler->gtNewIconNode(divisorValue, type); + // For TYP_INT, divisorValue was masked to UINT32_MAX above; if the + // original divisor has bit 31 set then divisorValue does not fit in + // int32_t. Sign-extend via int32_t so gtNewIconNode's FitsIn + // invariant holds and the constant round-trips correctly (analogous + // to the importercalls.cpp fix in #129136 for the Rotate{Left,Right} + // const-fold path). + ssize_t newDivisorValue = (type == TYP_INT) + ? static_cast(static_cast(divisorValue)) + : static_cast(divisorValue); + GenTree* divisor = m_compiler->gtNewIconNode(newDivisorValue, type); GenTree* mul = m_compiler->gtNewOperNode(GT_MUL, type, mulhi, divisor); dividend = m_compiler->gtNewLclvNode(dividend->AsLclVar()->GetLclNum(), dividend->TypeGet()); diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index c92f89d560bc2d..85fa0ed61192b9 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -812,7 +812,7 @@ void Lowering::LowerCast(GenTree* tree) convertIntrinsic = TargetArchitecture::Is64Bit ? NI_X86Base_X64_ConvertToInt64WithTruncation : NI_X86Base_ConvertToVector128Int32WithTruncation; - maxIntegralValue = m_compiler->gtNewIconNode(static_cast(UINT32_MAX)); + maxIntegralValue = m_compiler->gtNewIconNode(static_cast(UINT32_MAX)); minFloatOverflow = 4294967296.0; // 2^32; break; } diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_129099/Runtime_129099.cs b/src/tests/JIT/Regression/JitBlue/Runtime_129099/Runtime_129099.cs new file mode 100644 index 00000000000000..b05ef51e287e74 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_129099/Runtime_129099.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// NI_PRIMITIVE_RotateLeft/Right's const-fold path stored the unsigned +// fold result into a TYP_INT/TYP_UINT GenTreeIntCon via gtNewIconNode. +// For uint operands with the high bit set (e.g. RotateRight(0xFFFFFFFFu, k)) +// the zero-extended ssize_t value (0xFFFFFFFF = 4294967295) does not fit +// in int32_t, tripping a downstream FitsIn assert during +// 'Morph - Global' when the constant was bashed/updated. +// +// The volatile Sink is required so the fold result is materialized as a +// store (rather than dropped or inlined into the return path) -- that +// store is what hits the wide-value assert. + +namespace Runtime_129099; + +using System.Numerics; +using System.Runtime.CompilerServices; +using Xunit; + +public static class Runtime_129099 +{ + private static volatile uint SinkU32; + private static volatile int SinkI32; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint FoldRotateRightUInt() + { + uint v = BitOperations.RotateRight(0xFFFFFFFFu, 1); + SinkU32 = v; + return v; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint FoldRotateLeftUInt() + { + uint v = BitOperations.RotateLeft(0xFFFFFFFFu, 1); + SinkU32 = v; + return v; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint FoldRotateRightHighBit() + { + uint v = BitOperations.RotateRight(0x80000000u, 3); + SinkU32 = v; + return v; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int FoldIntRotateRightMinusOne() + { + int v = int.RotateRight(-1, 3); + SinkI32 = v; + return v; + } + + [Fact] + public static int TestEntryPoint() + { + if (FoldRotateRightUInt() != 0xFFFFFFFFu) return 101; + if (FoldRotateLeftUInt() != 0xFFFFFFFFu) return 102; + if (FoldRotateRightHighBit() != 0x10000000u) return 103; + if (FoldIntRotateRightMinusOne() != -1) return 104; + return 100; + } +} diff --git a/src/tests/JIT/Regression/Regression_ro_2.csproj b/src/tests/JIT/Regression/Regression_ro_2.csproj index 82e13ec3df7e5c..c53c1ef89dd13b 100644 --- a/src/tests/JIT/Regression/Regression_ro_2.csproj +++ b/src/tests/JIT/Regression/Regression_ro_2.csproj @@ -101,6 +101,7 @@ +