diff --git a/llvm/docs/GlobalISel/GenericOpcode.rst b/llvm/docs/GlobalISel/GenericOpcode.rst index 05d33ae817ea..53670c8561da 100644 --- a/llvm/docs/GlobalISel/GenericOpcode.rst +++ b/llvm/docs/GlobalISel/GenericOpcode.rst @@ -309,6 +309,16 @@ Take the minimum/maximum of two values. %5:_(s32) = G_SMIN %6, %2 +G_ABS +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Take the absolute value of a signed integer. The absolute value of the minimum +negative value (e.g. the 8-bit value `0x80`) is defined to be itself. + +.. code-block:: none + + %1:_(s32) = G_ABS %0 + G_UADDO, G_SADDO, G_USUBO, G_SSUBO, G_SMULO, G_UMULO ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/llvm/include/llvm/CodeGen/GlobalISel/IRTranslator.h b/llvm/include/llvm/CodeGen/GlobalISel/IRTranslator.h index b4ad3c5a2d48..718bba85d527 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/IRTranslator.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/IRTranslator.h @@ -289,6 +289,11 @@ class IRTranslator : public MachineFunctionPass { /// MachineBasicBlocks for the function have been created. void finishPendingPhis(); + /// Translate \p Inst into a unary operation \p Opcode. + /// \pre \p U is a unary operation. + bool translateUnaryOp(unsigned Opcode, const User &U, + MachineIRBuilder &MIRBuilder); + /// Translate \p Inst into a binary operation \p Opcode. /// \pre \p U is a binary operation. bool translateBinaryOp(unsigned Opcode, const User &U, diff --git a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h index 16fc2dda8eb0..d323ddc088dc 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h @@ -223,6 +223,7 @@ class MachineIRBuilder { protected: void validateTruncExt(const LLT Dst, const LLT Src, bool IsExtend); + void validateUnaryOp(const LLT Res, const LLT Op0); void validateBinaryOp(const LLT Res, const LLT Op0, const LLT Op1); void validateShiftOp(const LLT Res, const LLT Op0, const LLT Op1); @@ -1665,6 +1666,11 @@ class MachineIRBuilder { return buildInstr(TargetOpcode::G_UMAX, {Dst}, {Src0, Src1}); } + /// Build and insert \p Dst = G_ABS \p Src + MachineInstrBuilder buildAbs(const DstOp &Dst, const SrcOp &Src) { + return buildInstr(TargetOpcode::G_ABS, {Dst}, {Src}); + } + /// Build and insert \p Res = G_JUMP_TABLE \p JTI /// /// G_JUMP_TABLE sets \p Res to the address of the jump table specified by diff --git a/llvm/include/llvm/Support/TargetOpcodes.def b/llvm/include/llvm/Support/TargetOpcodes.def index 73671aefa4c4..ce1f92aca9bb 100644 --- a/llvm/include/llvm/Support/TargetOpcodes.def +++ b/llvm/include/llvm/Support/TargetOpcodes.def @@ -610,6 +610,9 @@ HANDLE_TARGET_OPCODE(G_UMIN) /// Generic unsigned integer maximum. HANDLE_TARGET_OPCODE(G_UMAX) +/// Generic integer absolute value. +HANDLE_TARGET_OPCODE(G_ABS) + /// Generic BRANCH instruction. This is an unconditional branch. HANDLE_TARGET_OPCODE(G_BR) diff --git a/llvm/include/llvm/Target/GenericOpcodes.td b/llvm/include/llvm/Target/GenericOpcodes.td index b5190054f6bc..87f0e4b61d31 100644 --- a/llvm/include/llvm/Target/GenericOpcodes.td +++ b/llvm/include/llvm/Target/GenericOpcodes.td @@ -414,6 +414,13 @@ def G_UMAX : GenericInstruction { let isCommutable = 1; } +// Generic integer absolute value. +def G_ABS : GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins type0:$src); + let hasSideEffects = 0; +} + //------------------------------------------------------------------------------ // Overflow ops //------------------------------------------------------------------------------ diff --git a/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td b/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td index 03fe1b21a339..c8715f01defd 100644 --- a/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td +++ b/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td @@ -135,6 +135,7 @@ def : GINodeEquiv; def : GINodeEquiv; def : GINodeEquiv; def : GINodeEquiv; +def : GINodeEquiv; def : GINodeEquiv; def : GINodeEquiv; def : GINodeEquiv; diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp index cb88a440c43e..5ef948b0fbeb 100644 --- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -295,7 +295,8 @@ bool IRTranslator::translateBinaryOp(unsigned Opcode, const User &U, return true; } -bool IRTranslator::translateFNeg(const User &U, MachineIRBuilder &MIRBuilder) { +bool IRTranslator::translateUnaryOp(unsigned Opcode, const User &U, + MachineIRBuilder &MIRBuilder) { Register Op0 = getOrCreateVReg(*U.getOperand(0)); Register Res = getOrCreateVReg(U); uint16_t Flags = 0; @@ -303,10 +304,14 @@ bool IRTranslator::translateFNeg(const User &U, MachineIRBuilder &MIRBuilder) { const Instruction &I = cast(U); Flags = MachineInstr::copyFlagsFromInstruction(I); } - MIRBuilder.buildFNeg(Res, Op0, Flags); + MIRBuilder.buildInstr(Opcode, {Res}, {Op0}, Flags); return true; } +bool IRTranslator::translateFNeg(const User &U, MachineIRBuilder &MIRBuilder) { + return translateUnaryOp(TargetOpcode::G_FNEG, U, MIRBuilder); +} + bool IRTranslator::translateCompare(const User &U, MachineIRBuilder &MIRBuilder) { auto *CI = dyn_cast(&U); @@ -1496,6 +1501,9 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID, return translateBinaryOp(TargetOpcode::G_SMIN, CI, MIRBuilder); case Intrinsic::smax: return translateBinaryOp(TargetOpcode::G_SMAX, CI, MIRBuilder); + case Intrinsic::abs: + // TODO: Preserve "int min is poison" arg in GMIR? + return translateUnaryOp(TargetOpcode::G_ABS, CI, MIRBuilder); case Intrinsic::smul_fix: return translateFixedPointIntrinsic(TargetOpcode::G_SMULFIX, CI, MIRBuilder); case Intrinsic::umul_fix: diff --git a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp index f025b06611e2..5dcfe93f9738 100644 --- a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp +++ b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp @@ -162,6 +162,11 @@ MachineInstrBuilder MachineIRBuilder::buildJumpTable(const LLT PtrTy, .addJumpTableIndex(JTI); } +void MachineIRBuilder::validateUnaryOp(const LLT Res, const LLT Op0) { + assert((Res.isScalar() || Res.isVector()) && "invalid operand type"); + assert((Res == Op0) && "type mismatch"); +} + void MachineIRBuilder::validateBinaryOp(const LLT Res, const LLT Op0, const LLT Op1) { assert((Res.isScalar() || Res.isVector()) && "invalid operand type"); @@ -949,6 +954,14 @@ MachineInstrBuilder MachineIRBuilder::buildInstr(unsigned Opc, SrcOps[1].getLLTTy(*getMRI()), SrcOps[2].getLLTTy(*getMRI())); break; } + case TargetOpcode::G_FNEG: + case TargetOpcode::G_ABS: + // All these are unary ops. + assert(DstOps.size() == 1 && "Invalid Dst"); + assert(SrcOps.size() == 1 && "Invalid Srcs"); + validateUnaryOp(DstOps[0].getLLTTy(*getMRI()), + SrcOps[0].getLLTTy(*getMRI())); + break; case TargetOpcode::G_ADD: case TargetOpcode::G_AND: case TargetOpcode::G_MUL: diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir index 535a17bd1158..058a8fcb0890 100644 --- a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir +++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir @@ -496,6 +496,9 @@ # DEBUG: G_UMAX (opcode {{[0-9]+}}): 1 type index # DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined # DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined +# DEBUG-NEXT: G_ABS (opcode {{[0-9]+}}): 1 type index, 0 imm indices +# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined +# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined # DEBUG-NEXT: G_BR (opcode {{[0-9]+}}): 0 type indices, 0 imm indices # DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined # DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined diff --git a/llvm/unittests/CodeGen/GlobalISel/MachineIRBuilderTest.cpp b/llvm/unittests/CodeGen/GlobalISel/MachineIRBuilderTest.cpp index 737e66273a35..8128c3390aa0 100644 --- a/llvm/unittests/CodeGen/GlobalISel/MachineIRBuilderTest.cpp +++ b/llvm/unittests/CodeGen/GlobalISel/MachineIRBuilderTest.cpp @@ -266,7 +266,7 @@ TEST_F(AArch64GISelMITest, BuildCasts) { EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF; } -TEST_F(AArch64GISelMITest, BuildMinMax) { +TEST_F(AArch64GISelMITest, BuildMinMaxAbs) { setUp(); if (!TM) return; @@ -279,6 +279,7 @@ TEST_F(AArch64GISelMITest, BuildMinMax) { B.buildSMax(S64, Copies[0], Copies[1]); B.buildUMin(S64, Copies[0], Copies[1]); B.buildUMax(S64, Copies[0], Copies[1]); + B.buildAbs(S64, Copies[0]); auto CheckStr = R"( ; CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPY $x0 @@ -287,6 +288,7 @@ TEST_F(AArch64GISelMITest, BuildMinMax) { ; CHECK: [[SMAX0:%[0-9]+]]:_(s64) = G_SMAX [[COPY0]]:_, [[COPY1]]:_ ; CHECK: [[UMIN0:%[0-9]+]]:_(s64) = G_UMIN [[COPY0]]:_, [[COPY1]]:_ ; CHECK: [[UMAX0:%[0-9]+]]:_(s64) = G_UMAX [[COPY0]]:_, [[COPY1]]:_ + ; CHECK: [[UABS0:%[0-9]+]]:_(s64) = G_ABS [[COPY0]]:_ )"; EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;