Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 59 additions & 7 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -36105,16 +36113,60 @@ 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;
}
Expand Down
150 changes: 150 additions & 0 deletions src/tests/JIT/Regression/JitBlue/Runtime_125160/Runtime_125160.cs
Original file line number Diff line number Diff line change
@@ -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>(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<float> left = Vector128.Create(1f, 2f, 3f, 4f);
Vector128<float> 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<float> 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<float> 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<double> left = Vector128.Create(1d, 2d);
Vector128<double> 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<double> foldedFalse = Avx.CompareScalar(left, right, FloatComparisonMode.OrderedFalseNonSignaling);
Assert.Equal(0d, foldedFalse.GetElement(0));
Assert.Equal(2d, foldedFalse.GetElement(1));

Vector128<double> 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<float> left = Vector512.Create(1f);
Vector512<float> 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<double> left = Vector512.Create(1d);
Vector512<double> 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));
}
}
1 change: 1 addition & 0 deletions src/tests/JIT/Regression/Regression_ro_2.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
<Compile Include="JitBlue\Runtime_129176\Runtime_129176.cs" />
<Compile Include="JitBlue\Runtime_129642\Runtime_129642.cs" />
<Compile Include="JitBlue\Runtime_10337\Runtime_10337.cs" />
<Compile Include="JitBlue\Runtime_125160\Runtime_125160.cs" />
</ItemGroup>
<Import Project="$(TestSourceDir)MergedTestRunner.targets" />
</Project>
Loading