Skip to content

arm64: Add support for Bitwise OR NOT & XOR NOT #111893

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

Merged
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
11 changes: 9 additions & 2 deletions src/coreclr/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2580,7 +2580,7 @@ void CodeGen::genCodeForMulHi(GenTreeOp* treeNode)
genProduceReg(treeNode);
}

// Generate code for ADD, SUB, MUL, DIV, UDIV, AND, AND_NOT, OR and XOR
// Generate code for ADD, SUB, MUL, DIV, UDIV, AND, AND_NOT, OR, OR_NOT, XOR and XOR_NOT
// This method is expected to have called genConsumeOperands() before calling it.
void CodeGen::genCodeForBinary(GenTreeOp* tree)
{
Expand All @@ -2589,7 +2589,8 @@ void CodeGen::genCodeForBinary(GenTreeOp* tree)
var_types targetType = tree->TypeGet();
emitter* emit = GetEmitter();

assert(tree->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_DIV, GT_UDIV, GT_AND, GT_AND_NOT, GT_OR, GT_XOR));
assert(tree->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_DIV, GT_UDIV, GT_AND, GT_AND_NOT, GT_OR, GT_OR_NOT, GT_XOR,
GT_XOR_NOT));

GenTree* op1 = tree->gtGetOp1();
GenTree* op2 = tree->gtGetOp2();
Expand Down Expand Up @@ -4276,6 +4277,9 @@ instruction CodeGen::genGetInsForOper(genTreeOps oper, var_types type)
case GT_OR:
ins = INS_orr;
break;
case GT_OR_NOT:
ins = INS_orn;
break;
case GT_ROR:
ins = INS_ror;
break;
Expand All @@ -4291,6 +4295,9 @@ instruction CodeGen::genGetInsForOper(genTreeOps oper, var_types type)
case GT_XOR:
ins = INS_eor;
break;
case GT_XOR_NOT:
ins = INS_eon;
break;

default:
NYI("Unhandled oper in genGetInsForOper() - integer");
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,9 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
break;

case GT_OR:
case GT_OR_NOT:
case GT_XOR:
case GT_XOR_NOT:
case GT_AND:
case GT_AND_NOT:
assert(varTypeIsIntegralOrI(treeNode));
Expand Down
5 changes: 4 additions & 1 deletion src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27752,7 +27752,8 @@ bool GenTreeHWIntrinsic::OperIsCreateScalarUnsafe() const
//
bool GenTreeHWIntrinsic::OperIsBitwiseHWIntrinsic(genTreeOps oper)
{
return (oper == GT_AND) || (oper == GT_AND_NOT) || (oper == GT_NOT) || (oper == GT_OR) || (oper == GT_XOR);
return (oper == GT_AND) || (oper == GT_AND_NOT) || (oper == GT_NOT) || (oper == GT_OR) || (oper == GT_OR_NOT) ||
(oper == GT_XOR) || (oper == GT_XOR_NOT);
}

//------------------------------------------------------------------------
Expand Down Expand Up @@ -31038,7 +31039,9 @@ bool GenTree::IsVectorPerElementMask(var_types simdBaseType, unsigned simdSize)
case GT_AND:
case GT_AND_NOT:
case GT_OR:
case GT_OR_NOT:
case GT_XOR:
case GT_XOR_NOT:
{
// We are a binary bitwise operation where both inputs are per-element masks
return intrinsic->Op(1)->IsVectorPerElementMask(simdBaseType, simdSize) &&
Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/jit/gtlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,12 @@ GTNODE(MUL_LONG , GenTreeOp ,1,0,GTK_BINOP|DBK_NOTHIR)
// AndNot - emitted on ARM/ARM64 as the BIC instruction. Also used for creating AndNot HWINTRINSIC vector nodes in a cross-ISA manner.
GTNODE(AND_NOT , GenTreeOp ,0,0,GTK_BINOP|DBK_NOTHIR)

// OrNot - emitted on ARM64 as the ORN instruction.
GTNODE(OR_NOT , GenTreeOp ,0,0,GTK_BINOP|DBK_NOTHIR)

// XorNot - emitted on ARM64 as the EON instruction.
GTNODE(XOR_NOT , GenTreeOp ,0,0,GTK_BINOP|DBK_NOTHIR)

#ifdef TARGET_ARM64
GTNODE(BFIZ , GenTreeOp ,0,0,GTK_BINOP|DBK_NOTHIR) // Bitfield Insert in Zero.
#endif
Expand Down
33 changes: 32 additions & 1 deletion src/coreclr/jit/lowerarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ bool Lowering::IsContainableUnaryOrBinaryOp(GenTree* parentNode, GenTree* childN
}
}

if (childNode->OperIs(GT_LSH, GT_RSH, GT_RSZ) && parentNode->OperIs(GT_NOT, GT_AND_NOT))
if (childNode->OperIs(GT_LSH, GT_RSH, GT_RSZ) && parentNode->OperIs(GT_NOT, GT_AND_NOT, GT_OR_NOT, GT_XOR_NOT))
{
return true;
}
Expand Down Expand Up @@ -652,6 +652,37 @@ GenTree* Lowering::LowerBinaryArithmetic(GenTreeOp* binOp)
return next;
}
}

if (binOp->OperIs(GT_OR, GT_XOR))
{
GenTree* opNode = nullptr;
GenTree* notNode = nullptr;
if (binOp->gtGetOp1()->OperIs(GT_NOT))
{
notNode = binOp->gtGetOp1();
opNode = binOp->gtGetOp2();
}
else if (binOp->gtGetOp2()->OperIs(GT_NOT))
{
notNode = binOp->gtGetOp2();
opNode = binOp->gtGetOp1();
}

if (notNode != nullptr)
{
binOp->gtOp1 = opNode;
binOp->gtOp2 = notNode->AsUnOp()->gtGetOp1();
if (binOp->OperIs(GT_OR))
{
binOp->ChangeOper(GT_OR_NOT);
}
else
{
binOp->ChangeOper(GT_XOR_NOT);
}
BlockRange().Remove(notNode);
}
}
#endif
}

Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/lsraarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -859,7 +859,9 @@ int LinearScan::BuildNode(GenTree* tree)
case GT_AND:
case GT_AND_NOT:
case GT_OR:
case GT_OR_NOT:
case GT_XOR:
case GT_XOR_NOT:
case GT_LSH:
case GT_RSH:
case GT_RSZ:
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9924,8 +9924,8 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node)
genTreeOps actualOper = node->GetOperForHWIntrinsicId(&isScalar);
genTreeOps oper = actualOper;

// We shouldn't find AND_NOT nodes since it should only be produced in lowering
assert(oper != GT_AND_NOT);
// We shouldn't find AND_NOT, OR_NOT or XOR_NOT nodes since it should only be produced in lowering
assert((oper != GT_AND_NOT) && (oper != GT_OR_NOT) && (oper != GT_XOR_NOT));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update the above comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


if (GenTreeHWIntrinsic::OperIsBitwiseHWIntrinsic(oper))
{
Expand Down
15 changes: 13 additions & 2 deletions src/coreclr/jit/simd.h
Original file line number Diff line number Diff line change
Expand Up @@ -704,8 +704,9 @@ void EvaluateUnarySimd(genTreeOps oper, bool scalar, var_types baseType, TSimd*

inline bool IsBinaryBitwiseOperation(genTreeOps oper)
{
return (oper == GT_AND) || (oper == GT_AND_NOT) || (oper == GT_LSH) || (oper == GT_OR) || (oper == GT_ROL) ||
(oper == GT_ROR) || (oper == GT_RSH) || (oper == GT_RSZ) || (oper == GT_XOR);
return (oper == GT_AND) || (oper == GT_AND_NOT) || (oper == GT_LSH) || (oper == GT_OR) || (oper == GT_OR_NOT) ||
(oper == GT_ROL) || (oper == GT_ROR) || (oper == GT_RSH) || (oper == GT_RSZ) || (oper == GT_XOR) ||
(oper == GT_XOR_NOT);
}

template <typename TBase>
Expand Down Expand Up @@ -842,6 +843,11 @@ TBase EvaluateBinaryScalarSpecialized(genTreeOps oper, TBase arg0, TBase arg1)
return arg0 | arg1;
}

case GT_OR_NOT:
{
return arg0 | ~arg1;
}

case GT_ROL:
{
// Normalize the "rotate by" value
Expand Down Expand Up @@ -901,6 +907,11 @@ TBase EvaluateBinaryScalarSpecialized(genTreeOps oper, TBase arg0, TBase arg1)
return arg0 ^ arg1;
}

case GT_XOR_NOT:
{
return arg0 ^ ~arg1;
}

default:
{
unreached();
Expand Down
8 changes: 4 additions & 4 deletions src/coreclr/jit/valuenum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8271,8 +8271,8 @@ ValueNum ValueNumStore::EvalHWIntrinsicFunBinary(

if (oper != GT_NONE)
{
// We shouldn't find AND_NOT nodes since it should only be produced in lowering
assert(oper != GT_AND_NOT);
// We shouldn't find AND_NOT, OR_NOT or XOR_NOT nodes since it should only be produced in lowering
assert((oper != GT_AND_NOT) && (oper != GT_OR_NOT) && (oper != GT_XOR_NOT));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here. please update the comment in this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


if (varTypeIsMask(type))
{
Expand Down Expand Up @@ -8415,7 +8415,7 @@ ValueNum ValueNumStore::EvalHWIntrinsicFunBinary(
genTreeOps oper = tree->GetOperForHWIntrinsicId(&isScalar);

// We shouldn't find AND_NOT nodes since it should only be produced in lowering
assert(oper != GT_AND_NOT);
assert((oper != GT_AND_NOT) && (oper != GT_OR_NOT) && (oper != GT_XOR_NOT));

if (isScalar)
{
Expand Down Expand Up @@ -8903,7 +8903,7 @@ ValueNum ValueNumStore::EvalHWIntrinsicFunBinary(
genTreeOps oper = tree->GetOperForHWIntrinsicId(&isScalar);

// We shouldn't find AND_NOT nodes since it should only be produced in lowering
assert(oper != GT_AND_NOT);
assert((oper != GT_AND_NOT) && (oper != GT_OR_NOT) && (oper != GT_XOR_NOT));

if (isScalar)
{
Expand Down
121 changes: 121 additions & 0 deletions src/tests/JIT/opt/InstructionCombining/Eon.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// 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 Xunit;

namespace TestEon
{
public class Program
{
[MethodImpl(MethodImplOptions.NoInlining)]
[Fact]
public static int CheckEon()
{
bool fail = false;

if (Eon(5, 1) != 0xFFFFFFFB)
{
fail = true;
}

if (EonLSL(0x12345678, 0xA) != 0xEDC92987)
{
fail = true;
}

if (EonLSLSwapInt(0x12345678, 0xA) != -0x1236d679)
{
fail = true;
}

if (EonLSLSwapUint(0xFDFDFDFD, 0xB) != 0x200C202)
{
fail = true;
}

if (EonLSR(0x87654321, 0xFEDCBA) != 0x789D4A3B)
{
fail = true;
}

if (EonASR(0x2468, 0xFEDCBA) != -0x246C)
{
fail = true;
}

if (EonLargeShift(0x87654321, 0x12345678) != 0xB89ABCDE)
{
fail = true;
}

if (EonLargeShift64Bit(0x1357135713571357, 0x123456789ABCDEF0) != 0xECA8ECA8ECE03DF1)
{
fail = true;
}

if (fail)
{
return 101;
}
return 100;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static uint Eon(uint a, uint b)
{
//ARM64-FULL-LINE: eon {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}
return a ^ ~b;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static uint EonLSL(uint a, uint b)
{
//ARM64-FULL-LINE: eon {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #14
return a ^ ~(b<<14);
}

[MethodImpl(MethodImplOptions.NoInlining)]
static int EonLSLSwapInt(int a, int b)
{
//ARM64-FULL-LINE: eon {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #14
return ~(b<<14) ^ a;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static uint EonLSLSwapUint(uint a, uint b)
{
//ARM64-FULL-LINE: eon {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #14
return ~(b<<14) ^ a;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static uint EonLSR(uint a, uint b)
{
//ARM64-FULL-LINE: eon {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSR #5
return a ^ ~(b>>5);
}

[MethodImpl(MethodImplOptions.NoInlining)]
static int EonASR(int a, int b)
{
//ARM64-FULL-LINE: eon {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, ASR #22
return a ^ ~(b>>22);
}

[MethodImpl(MethodImplOptions.NoInlining)]
static uint EonLargeShift(uint a, uint b)
{
//ARM64-FULL-LINE: eon {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #27
return a ^ ~(b<<123);
}

[MethodImpl(MethodImplOptions.NoInlining)]
static ulong EonLargeShift64Bit(ulong a, ulong b)
{
//ARM64-FULL-LINE: eon {{x[0-9]+}}, {{x[0-9]+}}, {{x[0-9]+}}, LSR #38
return a ^ ~(b>>166);
}
}
}
17 changes: 17 additions & 0 deletions src/tests/JIT/opt/InstructionCombining/Eon.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Needed for CLRTestEnvironmentVariable -->
<RequiresProcessIsolation>true</RequiresProcessIsolation>
</PropertyGroup>
<PropertyGroup>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="Eon.cs">
<HasDisasmCheck>true</HasDisasmCheck>
</Compile>
<CLRTestEnvironmentVariable Include="DOTNET_TieredCompilation" Value="0" />
<CLRTestEnvironmentVariable Include="DOTNET_JITMinOpts" Value="0" />
</ItemGroup>
</Project>
Loading
Loading