From 09305f3f3d134ee49a6db662607d842e87fd8ce5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Mar 2026 14:45:13 +0000 Subject: [PATCH 1/4] Initial plan From cf92236547b1171df53d1ff62054ba5a14cd1fac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:21:16 +0000 Subject: [PATCH 2/4] Fix CompareScalar folding with True/False comparison modes and add regression test CompareScalar preserves upper elements from the first operand, so folding to a full zero or AllBitsSet vector is incorrect. Skip folding for NI_AVX_CompareScalar with True/False modes so the hardware instruction produces the correct result. Fixes #125160 Co-authored-by: EgorBo <523221+EgorBo@users.noreply.github.com> --- src/coreclr/jit/gentree.cpp | 12 +++++ .../JitBlue/Runtime_125160/Runtime_125160.cs | 44 +++++++++++++++++++ .../JIT/Regression/Regression_ro_2.csproj | 1 + 3 files changed, 57 insertions(+) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_125160/Runtime_125160.cs diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 5a65ddcadf0943..299719dff4faeb 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -33828,6 +33828,12 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) case FloatComparisonMode::OrderedFalseNonSignaling: case FloatComparisonMode::OrderedFalseSignaling: { + if (ni == NI_AVX_CompareScalar) + { + // CompareScalar preserves upper elements from op1, + // so we can't fold to a full zero vector + break; + } resultNode = gtNewZeroConNode(retType); resultNode = gtWrapWithSideEffects(resultNode, tree, GTF_ALL_EFFECT); break; @@ -33836,6 +33842,12 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) case FloatComparisonMode::UnorderedTrueNonSignaling: case FloatComparisonMode::UnorderedTrueSignaling: { + if (ni == NI_AVX_CompareScalar) + { + // CompareScalar preserves upper elements from op1, + // so we can't fold to a full AllBitsSet vector + break; + } 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..eb8fa367971c0e --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_125160/Runtime_125160.cs @@ -0,0 +1,44 @@ +// 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 System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using Xunit; + +public class Runtime_125160 +{ + [ConditionalFact(typeof(Avx), nameof(Avx.IsSupported))] + public static void TestEntryPoint() + { + Assert.Equal(Vector128.Create(0f, 1f, 1f, 1f), CompareFalseFloat(Vector128.One, Vector128.Zero)); + Assert.Equal(Vector128.Create(0d, 1d), CompareFalseDouble(Vector128.One, Vector128.Zero)); + + Vector128 trueFloat = CompareTrueFloat(Vector128.One, Vector128.Zero); + Assert.Equal(BitConverter.UInt32BitsToSingle(0xFFFFFFFF), trueFloat.GetElement(0)); + Assert.Equal(1f, trueFloat.GetElement(1)); + Assert.Equal(1f, trueFloat.GetElement(2)); + Assert.Equal(1f, trueFloat.GetElement(3)); + + Vector128 trueDouble = CompareTrueDouble(Vector128.One, Vector128.Zero); + Assert.Equal(BitConverter.UInt64BitsToDouble(0xFFFFFFFFFFFFFFFF), trueDouble.GetElement(0)); + Assert.Equal(1d, trueDouble.GetElement(1)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static Vector128 CompareFalseFloat(Vector128 x, Vector128 y) + => Avx.CompareScalar(x, y, FloatComparisonMode.OrderedFalseSignaling); + + [MethodImpl(MethodImplOptions.NoInlining)] + static Vector128 CompareFalseDouble(Vector128 x, Vector128 y) + => Avx.CompareScalar(x, y, FloatComparisonMode.OrderedFalseNonSignaling); + + [MethodImpl(MethodImplOptions.NoInlining)] + static Vector128 CompareTrueFloat(Vector128 x, Vector128 y) + => Avx.CompareScalar(x, y, FloatComparisonMode.UnorderedTrueSignaling); + + [MethodImpl(MethodImplOptions.NoInlining)] + static Vector128 CompareTrueDouble(Vector128 x, Vector128 y) + => Avx.CompareScalar(x, y, FloatComparisonMode.UnorderedTrueNonSignaling); +} diff --git a/src/tests/JIT/Regression/Regression_ro_2.csproj b/src/tests/JIT/Regression/Regression_ro_2.csproj index 9920332e7f1c0f..5c62eb98295716 100644 --- a/src/tests/JIT/Regression/Regression_ro_2.csproj +++ b/src/tests/JIT/Regression/Regression_ro_2.csproj @@ -89,6 +89,7 @@ + From 01f88f36734a9837161f6d5ed3dca124e6bcd97f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Mar 2026 19:25:25 +0000 Subject: [PATCH 3/4] Also fix CompareMask folding with TYP_MASK return type and add AVX-512 test For NI_AVX512_CompareMask, retType is TYP_MASK which gtNewZeroConNode and gtNewAllBitsSetConNode don't handle (hitting unreached()). Use the SIMD vector type instead; the existing varTypeIsMask conversion at the end of gtFoldExprHWIntrinsic will wrap the result with CvtVectorToMask. Co-authored-by: EgorBo <523221+EgorBo@users.noreply.github.com> --- src/coreclr/jit/gentree.cpp | 12 +++++++-- .../JitBlue/Runtime_125160/Runtime_125160.cs | 25 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 299719dff4faeb..4f1f0395a7fc12 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -33834,7 +33834,11 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) // so we can't fold to a full zero vector break; } - resultNode = gtNewZeroConNode(retType); + // CompareMask returns TYP_MASK which gtNewZeroConNode doesn't handle, + // so use the SIMD vector type instead; the mask conversion at the end + // of this function will wrap it with CvtVectorToMask + resultNode = gtNewZeroConNode(varTypeIsMask(retType) ? getSIMDTypeForSize(simdSize) + : retType); resultNode = gtWrapWithSideEffects(resultNode, tree, GTF_ALL_EFFECT); break; } @@ -33848,7 +33852,11 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) // so we can't fold to a full AllBitsSet vector break; } - resultNode = gtNewAllBitsSetConNode(retType); + // CompareMask returns TYP_MASK which gtNewAllBitsSetConNode doesn't handle, + // so use the SIMD vector type instead; the mask conversion at the end + // of this function will wrap it with CvtVectorToMask + resultNode = gtNewAllBitsSetConNode(varTypeIsMask(retType) ? getSIMDTypeForSize(simdSize) + : 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 index eb8fa367971c0e..c341071b10de46 100644 --- a/src/tests/JIT/Regression/JitBlue/Runtime_125160/Runtime_125160.cs +++ b/src/tests/JIT/Regression/JitBlue/Runtime_125160/Runtime_125160.cs @@ -26,6 +26,15 @@ public static void TestEntryPoint() Assert.Equal(1d, trueDouble.GetElement(1)); } + [ConditionalFact(typeof(Avx512F), nameof(Avx512F.IsSupported))] + public static void TestCompareMask() + { + Assert.Equal(Vector512.Zero, CompareMaskFalseFloat(Vector512.One, Vector512.Zero)); + Assert.Equal(Vector512.Zero, CompareMaskFalseDouble(Vector512.One, Vector512.Zero)); + Assert.Equal(Vector512.Create(-1f), CompareMaskTrueFloat(Vector512.One, Vector512.Zero)); + Assert.Equal(Vector512.Create(-1d), CompareMaskTrueDouble(Vector512.One, Vector512.Zero)); + } + [MethodImpl(MethodImplOptions.NoInlining)] static Vector128 CompareFalseFloat(Vector128 x, Vector128 y) => Avx.CompareScalar(x, y, FloatComparisonMode.OrderedFalseSignaling); @@ -41,4 +50,20 @@ static Vector128 CompareTrueFloat(Vector128 x, Vector128 y) [MethodImpl(MethodImplOptions.NoInlining)] static Vector128 CompareTrueDouble(Vector128 x, Vector128 y) => Avx.CompareScalar(x, y, FloatComparisonMode.UnorderedTrueNonSignaling); + + [MethodImpl(MethodImplOptions.NoInlining)] + static Vector512 CompareMaskFalseFloat(Vector512 x, Vector512 y) + => Avx512F.Compare(x, y, FloatComparisonMode.OrderedFalseSignaling); + + [MethodImpl(MethodImplOptions.NoInlining)] + static Vector512 CompareMaskFalseDouble(Vector512 x, Vector512 y) + => Avx512F.Compare(x, y, FloatComparisonMode.OrderedFalseNonSignaling); + + [MethodImpl(MethodImplOptions.NoInlining)] + static Vector512 CompareMaskTrueFloat(Vector512 x, Vector512 y) + => Avx512F.Compare(x, y, FloatComparisonMode.UnorderedTrueSignaling); + + [MethodImpl(MethodImplOptions.NoInlining)] + static Vector512 CompareMaskTrueDouble(Vector512 x, Vector512 y) + => Avx512F.Compare(x, y, FloatComparisonMode.UnorderedTrueNonSignaling); } From 1826aad96488db6ee4ed6146835f3fa1c1de3ee4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Mar 2026 23:49:42 +0000 Subject: [PATCH 4/4] Fix TestCompareMask: use AllBitsSet instead of Create(-1f) for UnorderedTrue assertions Vector512.Create(-1f) has bit pattern 0xBF800000 per element, but UnorderedTrue comparison modes produce all-bits-set (0xFFFFFFFF per element, which is NaN). Use Vector512.AllBitsSet to correctly match the expected result. Co-authored-by: EgorBo <523221+EgorBo@users.noreply.github.com> --- .../JIT/Regression/JitBlue/Runtime_125160/Runtime_125160.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_125160/Runtime_125160.cs b/src/tests/JIT/Regression/JitBlue/Runtime_125160/Runtime_125160.cs index c341071b10de46..aace13ceda7813 100644 --- a/src/tests/JIT/Regression/JitBlue/Runtime_125160/Runtime_125160.cs +++ b/src/tests/JIT/Regression/JitBlue/Runtime_125160/Runtime_125160.cs @@ -31,8 +31,8 @@ public static void TestCompareMask() { Assert.Equal(Vector512.Zero, CompareMaskFalseFloat(Vector512.One, Vector512.Zero)); Assert.Equal(Vector512.Zero, CompareMaskFalseDouble(Vector512.One, Vector512.Zero)); - Assert.Equal(Vector512.Create(-1f), CompareMaskTrueFloat(Vector512.One, Vector512.Zero)); - Assert.Equal(Vector512.Create(-1d), CompareMaskTrueDouble(Vector512.One, Vector512.Zero)); + Assert.Equal(Vector512.AllBitsSet, CompareMaskTrueFloat(Vector512.One, Vector512.Zero)); + Assert.Equal(Vector512.AllBitsSet, CompareMaskTrueDouble(Vector512.One, Vector512.Zero)); } [MethodImpl(MethodImplOptions.NoInlining)]