diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 9cf0a130006d4d..3bf17837720256 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -2518,9 +2518,17 @@ ValueNum ValueNumStore::VNOneForSimdType(var_types simdType, var_types simdBaseT return VNBroadcastForSimdType(simdType, simdBaseType, oneVN); } -ValueNum ValueNumStore::VNForSimdType(unsigned simdSize, var_types simdBaseType) +ValueNum ValueNumStore::VNForSimdType(unsigned simdSize, var_types simdBaseType, var_types auxiliaryType) { - ValueNum baseTypeVN = VNForIntCon(INT32(simdBaseType)); + // Both types fit in a byte. Reserve zero in the upper byte for no auxiliary type, + // preserving the base-type constant for the common case. + unsigned encodedTypes = static_cast(simdBaseType); + if (auxiliaryType != TYP_UNKNOWN) + { + encodedTypes |= (static_cast(auxiliaryType) + 1) << SimdTypeBits; + } + + ValueNum baseTypeVN = VNForIntCon(encodedTypes); ValueNum sizeVN = VNForIntCon(simdSize); ValueNum simdTypeVN = VNForFunc(TYP_REF, VNF_SimdType, sizeVN, baseTypeVN); @@ -10932,7 +10940,7 @@ uint32_t ValueNumStore::GetVNHWIntrinsicSizeAndBaseType(const VNFuncApp& funcApp assert(IsVNConstant(simdType.GetArg(0))); assert(IsVNConstant(simdType.GetArg(1))); - *simdBaseType = static_cast(GetConstantInt32(simdType.GetArg(1))); + *simdBaseType = static_cast(GetConstantInt32(simdType.GetArg(1)) & SimdTypeMask); return static_cast(GetConstantInt32(simdType.GetArg(0))); } #endif // FEATURE_HW_INTRINSICS @@ -11422,11 +11430,19 @@ void ValueNumStore::vnDumpSimdType(Compiler* comp, VNFuncApp* simdType) assert(IsVNConstant(simdType->GetArg(0))); assert(IsVNConstant(simdType->GetArg(1))); - int simdSize = ConstantValue(simdType->GetArg(0)); - var_types simdBaseType = static_cast(ConstantValue(simdType->GetArg(1))); + int simdSize = ConstantValue(simdType->GetArg(0)); + unsigned encodedTypes = ConstantValue(simdType->GetArg(1)); + var_types simdBaseType = static_cast(encodedTypes & SimdTypeMask); + unsigned auxiliaryType = encodedTypes >> SimdTypeBits; - printf("%s(simd%d, %s)", VNFuncName(simdType->GetFunc()), simdSize, + printf("%s(simd%d, %s", VNFuncName(simdType->GetFunc()), simdSize, (simdBaseType == TYP_UNDEF) ? varTypeName(TYP_UNDEF) : varTypeName(simdBaseType)); + + if (auxiliaryType != 0) + { + printf(", aux %s", varTypeName(static_cast(auxiliaryType - 1))); + } + printf(")"); } #endif // FEATURE_SIMD @@ -14188,8 +14204,10 @@ void Compiler::fgValueNumberHWIntrinsic(GenTreeHWIntrinsic* tree) } else { - VNFunc func = GetVNFuncForNode(tree); - ValueNum simdTypeVN = vnStore->VNForSimdType(tree->GetSimdSize(), tree->GetSimdBaseType()); + // The auxiliary type distinguishes overloads with the same operand bits, such as gather index widths. + VNFunc func = GetVNFuncForNode(tree); + ValueNum simdTypeVN = + vnStore->VNForSimdType(tree->GetSimdSize(), tree->GetSimdBaseType(), tree->GetAuxiliaryType()); ValueNumPair resultTypeVNPair(simdTypeVN, simdTypeVN); JITDUMP(" simdTypeVN is "); diff --git a/src/coreclr/jit/valuenum.h b/src/coreclr/jit/valuenum.h index 59234ee2f59a5b..a21b0582ccab52 100644 --- a/src/coreclr/jit/valuenum.h +++ b/src/coreclr/jit/valuenum.h @@ -396,6 +396,12 @@ class ValueNumStore VCA_ReservedBits = 0x01, // i.e. (VCA_UnsignedSrc) }; +#ifdef FEATURE_SIMD + static constexpr unsigned SimdTypeBits = 8; + static constexpr unsigned SimdTypeMask = (1 << SimdTypeBits) - 1; + static_assert(TYP_COUNT <= SimdTypeMask); +#endif + // Helpers and an array of length GT_COUNT, mapping genTreeOp values to their VNFOpAttrib. static constexpr uint8_t GetOpAttribsForArity(genTreeOps oper, GenTreeOperKind kind); static constexpr uint8_t GetOpAttribsForGenTree(genTreeOps oper, @@ -792,7 +798,7 @@ class ValueNumStore ValueNum VNOneForSimdType(var_types simdType, var_types simdBaseType); // A helper function for constructing VNF_SimdType VNs. - ValueNum VNForSimdType(unsigned simdSize, var_types simdBaseType); + ValueNum VNForSimdType(unsigned simdSize, var_types simdBaseType, var_types auxiliaryType); // Returns if a value number represents NaN in all elements bool VNIsVectorNaN(var_types simdType, var_types simdBaseType, ValueNum valVN); diff --git a/src/tests/JIT/Regression_ro_2/Runtime_134486.cs b/src/tests/JIT/Regression_ro_2/Runtime_134486.cs new file mode 100644 index 00000000000000..2d938c74f6d568 --- /dev/null +++ b/src/tests/JIT/Regression_ro_2/Runtime_134486.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using System.Runtime.Intrinsics.X86; +using Xunit; + +public class Runtime_134486 +{ + [ConditionalFact(typeof(Avx2), nameof(Avx2.IsSupported))] + public static unsafe void TestGather() + { + int* data = stackalloc int[] { 10, 11, 12, 13 }; + Assert.Equal(2, Gather(data, Vector128.Create(1, 0, 2, 0), Vector128.Create(11, 10, 12, 10))); + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + private static unsafe int Gather(int* data, Vector128 indices, Vector128 expected) + { + Vector128 a = Avx2.GatherVector128(data, indices, 4); + Vector128 b = Avx2.GatherVector128(data, indices.AsInt64(), 4); + + if (a == expected) + { + return b == expected ? 1 : 2; + } + return 3; + } + + [ConditionalFact(typeof(AdvSimd.Arm64), nameof(AdvSimd.Arm64.IsSupported))] + public static void TestAddSaturateScalar() + { + Assert.True(DistinguishAddSaturateScalar(Vector64.CreateScalar(1), Vector64.CreateScalar(-1))); + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + private static bool DistinguishAddSaturateScalar(Vector64 left, Vector64 right) + { + return AdvSimd.Arm64.AddSaturateScalar(left, right) != AdvSimd.Arm64.AddSaturateScalar(left, right.AsUInt32()); + } +}