diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 830067396011c0..6fcafff826b144 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -9459,6 +9459,14 @@ GenTree* Compiler::gtNewZeroConNode(var_types type) } #endif // FEATURE_SIMD +#if defined(FEATURE_MASKED_HW_INTRINSICS) + if (varTypeIsMask(type)) + { + // The GenTreeMskCon constructor already zero-initializes the mask value. + return gtNewMskConNode(type); + } +#endif // FEATURE_MASKED_HW_INTRINSICS + type = genActualType(type); switch (type) { @@ -36105,16 +36113,61 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) { case FloatComparisonMode::OrderedFalseNonSignaling: case FloatComparisonMode::OrderedFalseSignaling: - { - resultNode = gtNewZeroConNode(retType); - resultNode = gtWrapWithSideEffects(resultNode, tree, GTF_ALL_EFFECT); - break; - } - case FloatComparisonMode::UnorderedTrueNonSignaling: case FloatComparisonMode::UnorderedTrueSignaling: { - resultNode = gtNewAllBitsSetConNode(retType); + bool isFalse = (mode == FloatComparisonMode::OrderedFalseNonSignaling) || + (mode == FloatComparisonMode::OrderedFalseSignaling); + + if (ni == NI_AVX_CompareScalar) + { + // CompareScalar only updates the lowest element and copies the remaining + // (upper) elements from op1 unchanged. We can therefore only constant fold + // this when op1 is itself a constant so that those upper elements are known. + + if (!op1->IsCnsVec()) + { + break; + } + + GenTreeVecCon* vecCon = op1->AsVecCon(); + + // Set the lowest element to the comparison result (all zeros for a false + // mode, all ones for a true mode) while leaving the upper elements intact. + + if (simdBaseType == TYP_FLOAT) + { + vecCon->gtSimdVal.u32[0] = isFalse ? 0 : 0xFFFFFFFF; + } + else + { + assert(simdBaseType == TYP_DOUBLE); + vecCon->gtSimdVal.u64[0] = isFalse ? 0 : 0xFFFFFFFFFFFFFFFF; + } + + fgUpdateConstTreeValueNumber(vecCon); + resultNode = vecCon; + } + else if (isFalse) + { + resultNode = gtNewZeroConNode(retType); + } +#if defined(FEATURE_MASKED_HW_INTRINSICS) + else if (varTypeIsMask(retType)) + { + // A true comparison produces an all-true mask for the given element count. + + GenTreeMskCon* mskCon = gtNewMskConNode(retType); + mskCon->gtSimdMaskVal = + simdmask_t::AllBitsSet(GenTreeVecCon::ElementCount(simdSize, simdBaseType)); + resultNode = mskCon; + } +#endif // FEATURE_MASKED_HW_INTRINSICS + else + { + resultNode = gtNewAllBitsSetConNode(retType); + } + resultNode = gtWrapWithSideEffects(resultNode, tree, GTF_ALL_EFFECT); break; } diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_125160/Runtime_125160.cs b/src/tests/JIT/Regression/JitBlue/Runtime_125160/Runtime_125160.cs new file mode 100644 index 00000000000000..dcf7daad8bdc1d --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_125160/Runtime_125160.cs @@ -0,0 +1,150 @@ +// 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.X86; +using Xunit; + +public class Runtime_125160 +{ + // The True/False float comparison modes always produce the same result regardless of the + // inputs. The JIT constant folds these when the operands are constant. For the scalar form + // (CompareScalar) only the lowest element is set to the comparison result; the upper elements + // must be copied unchanged from op1. This test ensures the folded result matches the result + // computed at runtime from non-constant operands. + + [MethodImpl(MethodImplOptions.NoInlining)] + private static T Opaque(T value) => value; + + [Fact] + public static void TestEntryPoint() + { + if (Avx.IsSupported) + { + TestCompareScalarSingle(); + TestCompareScalarDouble(); + } + + if (Avx512F.IsSupported) + { + TestCompareSingle(); + TestCompareDouble(); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void TestCompareScalarSingle() + { + Vector128 left = Vector128.Create(1f, 2f, 3f, 4f); + Vector128 right = Vector128.Create(5f, 6f, 7f, 8f); + + // OrderedFalseNonSignaling: lowest element is all-zero, upper elements copied from op1. + Assert.Equal( + Avx.CompareScalar(Opaque(left), Opaque(right), FloatComparisonMode.OrderedFalseNonSignaling), + Avx.CompareScalar(left, right, FloatComparisonMode.OrderedFalseNonSignaling)); + + Assert.Equal( + Avx.CompareScalar(Opaque(left), Opaque(right), FloatComparisonMode.OrderedFalseSignaling), + Avx.CompareScalar(left, right, FloatComparisonMode.OrderedFalseSignaling)); + + // UnorderedTrueNonSignaling: lowest element is all-ones, upper elements copied from op1. + Assert.Equal( + Avx.CompareScalar(Opaque(left), Opaque(right), FloatComparisonMode.UnorderedTrueNonSignaling), + Avx.CompareScalar(left, right, FloatComparisonMode.UnorderedTrueNonSignaling)); + + Assert.Equal( + Avx.CompareScalar(Opaque(left), Opaque(right), FloatComparisonMode.UnorderedTrueSignaling), + Avx.CompareScalar(left, right, FloatComparisonMode.UnorderedTrueSignaling)); + + // Explicitly validate that the upper elements are preserved and the low element is set. + Vector128 foldedFalse = Avx.CompareScalar(left, right, FloatComparisonMode.OrderedFalseNonSignaling); + Assert.Equal(0f, foldedFalse.GetElement(0)); + Assert.Equal(2f, foldedFalse.GetElement(1)); + Assert.Equal(3f, foldedFalse.GetElement(2)); + Assert.Equal(4f, foldedFalse.GetElement(3)); + + Vector128 foldedTrue = Avx.CompareScalar(left, right, FloatComparisonMode.UnorderedTrueNonSignaling); + Assert.Equal(-1, foldedTrue.AsInt32().GetElement(0)); + Assert.Equal(2f, foldedTrue.GetElement(1)); + Assert.Equal(3f, foldedTrue.GetElement(2)); + Assert.Equal(4f, foldedTrue.GetElement(3)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void TestCompareScalarDouble() + { + Vector128 left = Vector128.Create(1d, 2d); + Vector128 right = Vector128.Create(3d, 4d); + + Assert.Equal( + Avx.CompareScalar(Opaque(left), Opaque(right), FloatComparisonMode.OrderedFalseNonSignaling), + Avx.CompareScalar(left, right, FloatComparisonMode.OrderedFalseNonSignaling)); + + Assert.Equal( + Avx.CompareScalar(Opaque(left), Opaque(right), FloatComparisonMode.OrderedFalseSignaling), + Avx.CompareScalar(left, right, FloatComparisonMode.OrderedFalseSignaling)); + + Assert.Equal( + Avx.CompareScalar(Opaque(left), Opaque(right), FloatComparisonMode.UnorderedTrueNonSignaling), + Avx.CompareScalar(left, right, FloatComparisonMode.UnorderedTrueNonSignaling)); + + Assert.Equal( + Avx.CompareScalar(Opaque(left), Opaque(right), FloatComparisonMode.UnorderedTrueSignaling), + Avx.CompareScalar(left, right, FloatComparisonMode.UnorderedTrueSignaling)); + + Vector128 foldedFalse = Avx.CompareScalar(left, right, FloatComparisonMode.OrderedFalseNonSignaling); + Assert.Equal(0d, foldedFalse.GetElement(0)); + Assert.Equal(2d, foldedFalse.GetElement(1)); + + Vector128 foldedTrue = Avx.CompareScalar(left, right, FloatComparisonMode.UnorderedTrueNonSignaling); + Assert.Equal(-1L, foldedTrue.AsInt64().GetElement(0)); + Assert.Equal(2d, foldedTrue.GetElement(1)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void TestCompareSingle() + { + Vector512 left = Vector512.Create(1f); + Vector512 right = Vector512.Create(2f); + + Assert.Equal( + Avx512F.Compare(Opaque(left), Opaque(right), FloatComparisonMode.OrderedFalseNonSignaling), + Avx512F.Compare(left, right, FloatComparisonMode.OrderedFalseNonSignaling)); + + Assert.Equal( + Avx512F.Compare(Opaque(left), Opaque(right), FloatComparisonMode.OrderedFalseSignaling), + Avx512F.Compare(left, right, FloatComparisonMode.OrderedFalseSignaling)); + + Assert.Equal( + Avx512F.Compare(Opaque(left), Opaque(right), FloatComparisonMode.UnorderedTrueNonSignaling), + Avx512F.Compare(left, right, FloatComparisonMode.UnorderedTrueNonSignaling)); + + Assert.Equal( + Avx512F.Compare(Opaque(left), Opaque(right), FloatComparisonMode.UnorderedTrueSignaling), + Avx512F.Compare(left, right, FloatComparisonMode.UnorderedTrueSignaling)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void TestCompareDouble() + { + Vector512 left = Vector512.Create(1d); + Vector512 right = Vector512.Create(2d); + + Assert.Equal( + Avx512F.Compare(Opaque(left), Opaque(right), FloatComparisonMode.OrderedFalseNonSignaling), + Avx512F.Compare(left, right, FloatComparisonMode.OrderedFalseNonSignaling)); + + Assert.Equal( + Avx512F.Compare(Opaque(left), Opaque(right), FloatComparisonMode.OrderedFalseSignaling), + Avx512F.Compare(left, right, FloatComparisonMode.OrderedFalseSignaling)); + + Assert.Equal( + Avx512F.Compare(Opaque(left), Opaque(right), FloatComparisonMode.UnorderedTrueNonSignaling), + Avx512F.Compare(left, right, FloatComparisonMode.UnorderedTrueNonSignaling)); + + Assert.Equal( + Avx512F.Compare(Opaque(left), Opaque(right), FloatComparisonMode.UnorderedTrueSignaling), + Avx512F.Compare(left, right, FloatComparisonMode.UnorderedTrueSignaling)); + } +} diff --git a/src/tests/JIT/Regression/Regression_ro_2.csproj b/src/tests/JIT/Regression/Regression_ro_2.csproj index 635e5703ed63be..32f95a37d5e96a 100644 --- a/src/tests/JIT/Regression/Regression_ro_2.csproj +++ b/src/tests/JIT/Regression/Regression_ro_2.csproj @@ -105,6 +105,7 @@ +