Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add flags checks to BMI1 intrinsic lowering #66736

Merged
merged 3 commits into from
Mar 20, 2022
Merged
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
19 changes: 19 additions & 0 deletions src/coreclr/jit/lowerxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3789,6 +3789,13 @@ GenTree* Lowering::TryLowerAndOpToResetLowestSetBit(GenTreeOp* andNode)
return nullptr;
}

// Subsequent nodes may rely on CPU flags set by these nodes in which case we cannot remove them
if (((addOp2->gtFlags & GTF_SET_FLAGS) != 0) || ((op2->gtFlags & GTF_SET_FLAGS) != 0) ||
((andNode->gtFlags & GTF_SET_FLAGS) != 0))
{
return nullptr;
}

NamedIntrinsic intrinsic;
if (op1->TypeIs(TYP_LONG) && comp->compOpportunisticallyDependsOn(InstructionSet_BMI1_X64))
{
Expand Down Expand Up @@ -3868,6 +3875,12 @@ GenTree* Lowering::TryLowerAndOpToExtractLowestSetBit(GenTreeOp* andNode)
return nullptr;
}

// Subsequent nodes may rely on CPU flags set by these nodes in which case we cannot remove them
if (((opNode->gtFlags & GTF_SET_FLAGS) != 0) || ((negNode->gtFlags & GTF_SET_FLAGS) != 0))
{
return nullptr;
}

NamedIntrinsic intrinsic;
if (andNode->TypeIs(TYP_LONG) && comp->compOpportunisticallyDependsOn(InstructionSet_BMI1_X64))
{
Expand Down Expand Up @@ -3947,6 +3960,12 @@ GenTree* Lowering::TryLowerAndOpToAndNot(GenTreeOp* andNode)
return nullptr;
}

// Subsequent nodes may rely on CPU flags set by these nodes in which case we cannot remove them
if (((andNode->gtFlags & GTF_SET_FLAGS) != 0) || ((notNode->gtFlags & GTF_SET_FLAGS) != 0))
{
return nullptr;
}

NamedIntrinsic intrinsic;
if (andNode->TypeIs(TYP_LONG) && comp->compOpportunisticallyDependsOn(InstructionSet_BMI1_X64))
{
Expand Down
65 changes: 56 additions & 9 deletions src/tests/JIT/Intrinsics/BMI1Intrinsics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,76 @@ static int Main(string[] args)

foreach (var value in values)
{
Test(value.input1, AndNot(value.input1, value.input2), value.andnExpected, nameof(AndNot));
Test(value.input1, ExtractLowestSetIsolatedBit(value.input1), value.blsiExpected, nameof(ExtractLowestSetIsolatedBit));
Test(value.input1, ResetLowestSetBit(value.input1), value.blsrExpected, nameof(ResetLowestSetBit));
Test(value.input1, GetMaskUpToLowestSetBit(value.input1), value.blmskExpected, nameof(GetMaskUpToLowestSetBit));
Test(value.input1, AndNot_32bit(value.input1, value.input2), value.andnExpected, nameof(AndNot_32bit));
Test(value.input1, ExtractLowestSetIsolatedBit_32bit(value.input1), value.blsiExpected, nameof(ExtractLowestSetIsolatedBit_32bit));
Test(value.input1, ResetLowestSetBit_32bit(value.input1), value.blsrExpected, nameof(ResetLowestSetBit_32bit));
Test(value.input1, GetMaskUpToLowestSetBit_32bit(value.input1), value.blmskExpected, nameof(GetMaskUpToLowestSetBit_32bit));
}


var values2 = new (ulong input1, ulong input2, ulong andnExpected, ulong blsiExpected, ulong blsrExpected, ulong blmskExpected)[] {
(0, 0, 0, 0, 0, 0),
(1, 0, 1, 1, 0,0xFFFFFFFF_FFFFFFFE),
(ulong.MaxValue / 2, 0,0x7FFFFFFF_FFFFFFFF, 1,0x7FFFFFFF_FFFFFFFE,0xFFFFFFFF_FFFFFFFE),
((ulong.MaxValue / 2) - 1, 0,0x7FFFFFFF_FFFFFFFE, 2,0x7FFFFFFF_FFFFFFFC,0xFFFFFFFF_FFFFFFFC),
((ulong.MaxValue / 2) + 1, 0,0x80000000_00000000,0x80000000_00000000, 0, 0),
(ulong.MaxValue - 1, 0,0xFFFFFFFF_FFFFFFFE, 2,0xFFFFFFFF_FFFFFFFC,0xFFFFFFFF_FFFFFFFC),
(ulong.MaxValue, 0,0xFFFFFFFF_FFFFFFFF, 1,0xFFFFFFFF_FFFFFFFE,0xFFFFFFFF_FFFFFFFE),
(0xAAAAAAAA_AAAAAAAA,0xAAAAAAAA_AAAAAAAA, 0, 2,0xAAAAAAAA_AAAAAAA8,0xFFFFFFFF_FFFFFFFC),
(0xAAAAAAAA_AAAAAAAA,0x55555555_55555555,0xAAAAAAAA_AAAAAAAA, 2,0xAAAAAAAA_AAAAAAA8,0xFFFFFFFF_FFFFFFFC),
};

foreach (var value in values2)
{
Test(value.input1, AndNot_64bit(value.input1, value.input2), value.andnExpected, nameof(AndNot_64bit));
Test(value.input1, ExtractLowestSetIsolatedBit_64bit(value.input1), value.blsiExpected, nameof(ExtractLowestSetIsolatedBit_64bit));
Test(value.input1, ResetLowestSetBit_64bit(value.input1), value.blsrExpected, nameof(ResetLowestSetBit_64bit));
Test(value.input1, GetMaskUpToLowestSetBit_64bit(value.input1), value.blmskExpected, nameof(GetMaskUpToLowestSetBit_64bit));
}

return _errorCode;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static uint AndNot(uint x, uint y) => x & (~y); // bmi1 andn
private static uint AndNot_32bit(uint x, uint y) => x & (~y); // bmi1 andn

[MethodImpl(MethodImplOptions.NoInlining)]
private static ulong AndNot_64bit(ulong x, ulong y) => x & (~y); // bmi1 andn

[MethodImpl(MethodImplOptions.NoInlining)]
private static uint ExtractLowestSetIsolatedBit_32bit(uint x) => (uint)(x & (-x)); // bmi1 blsi

[MethodImpl(MethodImplOptions.NoInlining)]
private static ulong ExtractLowestSetIsolatedBit_64bit(ulong x) => x & (ulong)(-(long)x); // bmi1 blsi

[MethodImpl(MethodImplOptions.NoInlining)]
private static uint ResetLowestSetBit_32bit(uint x) => x & (x - 1); // bmi1 blsr

[MethodImpl(MethodImplOptions.NoInlining)]
private static uint ExtractLowestSetIsolatedBit(uint x) => (uint)(x & (-x)); // bmi1 blsi
private static ulong ResetLowestSetBit_64bit(ulong x) => x & (x - 1); // bmi1 blsr

[MethodImpl(MethodImplOptions.NoInlining)]
private static uint ResetLowestSetBit(uint x) => x & (x - 1); // bmi1 blsr
private static uint GetMaskUpToLowestSetBit_32bit(uint x) => (uint)(x ^ (-x)); // bmi1 blmsk

[MethodImpl(MethodImplOptions.NoInlining)]
private static uint GetMaskUpToLowestSetBit(uint x) => (uint)(x ^ (-x)); // bmi1 blmsk
private static ulong GetMaskUpToLowestSetBit_64bit(ulong x) => x ^ (ulong)(-(long)x); // bmi1 blmsk

[MethodImpl(MethodImplOptions.NoInlining)]
private static void Test(uint input, uint output, uint expected, string callerName)
{
if (output != expected)
{
Console.WriteLine($"{callerName} failed.");
Console.WriteLine($"Input: {input:X}");
Console.WriteLine($"Output: {output:X}");
Console.WriteLine($"Expected: {expected:X}");

_errorCode++;
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void Test(uint input, uint output, uint expected,string callerName)
private static void Test(ulong input, ulong output, ulong expected, string callerName)
{
if (output != expected)
{
Expand Down