diff --git a/llvm/docs/GlobalISel/GenericOpcode.rst b/llvm/docs/GlobalISel/GenericOpcode.rst index b05394aeee003..4ef16ffc69bd8 100644 --- a/llvm/docs/GlobalISel/GenericOpcode.rst +++ b/llvm/docs/GlobalISel/GenericOpcode.rst @@ -348,6 +348,26 @@ G_ICMP Perform integer comparison producing non-zero (true) or zero (false). It's target specific whether a true value is 1, ~0U, or some other non-zero value. +G_SCMP +^^^^^^ + +Perform signed 3-way integer comparison producing -1 (smaller), 0 (equal), or 1 (larger). + +.. code-block:: none + + %5:_(s32) = G_SCMP %6, %2 + + +G_UCMP +^^^^^^ + +Perform unsigned 3-way integer comparison producing -1 (smaller), 0 (equal), or 1 (larger). + +.. code-block:: none + + %7:_(s32) = G_UCMP %2, %6 + + G_SELECT ^^^^^^^^ diff --git a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h index e74136f34b234..56a77b8596a18 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h @@ -1273,6 +1273,34 @@ class MachineIRBuilder { const SrcOp &Op0, const SrcOp &Op1, std::optional Flags = std::nullopt); + /// Build and insert a \p Res = G_SCMP \p Op0, \p Op1 + /// + /// \pre setBasicBlock or setMI must have been called. + + /// \pre \p Res must be a generic virtual register with scalar or + /// vector type. Typically this starts as s2 or . + /// \pre \p Op0 and Op1 must be generic virtual registers with the + /// same number of elements as \p Res. If \p Res is a scalar, + /// \p Op0 must be a scalar. + /// + /// \return a MachineInstrBuilder for the newly created instruction. + MachineInstrBuilder buildSCmp(const DstOp &Res, const SrcOp &Op0, + const SrcOp &Op1); + + /// Build and insert a \p Res = G_UCMP \p Op0, \p Op1 + /// + /// \pre setBasicBlock or setMI must have been called. + + /// \pre \p Res must be a generic virtual register with scalar or + /// vector type. Typically this starts as s2 or . + /// \pre \p Op0 and Op1 must be generic virtual registers with the + /// same number of elements as \p Res. If \p Res is a scalar, + /// \p Op0 must be a scalar. + /// + /// \return a MachineInstrBuilder for the newly created instruction. + MachineInstrBuilder buildUCmp(const DstOp &Res, const SrcOp &Op0, + const SrcOp &Op1); + /// Build and insert a \p Res = G_IS_FPCLASS \p Src, \p Mask MachineInstrBuilder buildIsFPClass(const DstOp &Res, const SrcOp &Src, unsigned Mask) { diff --git a/llvm/include/llvm/Support/TargetOpcodes.def b/llvm/include/llvm/Support/TargetOpcodes.def index e7f40e87ed24a..ba3713cd68506 100644 --- a/llvm/include/llvm/Support/TargetOpcodes.def +++ b/llvm/include/llvm/Support/TargetOpcodes.def @@ -503,6 +503,12 @@ HANDLE_TARGET_OPCODE(G_ICMP) /// Generic floating-point comparison, also applicable to vectors. HANDLE_TARGET_OPCODE(G_FCMP) +/// Generic signed 3-way comparison. +HANDLE_TARGET_OPCODE(G_SCMP) + +/// Generic unsigned 3-way comparison. +HANDLE_TARGET_OPCODE(G_UCMP) + /// Generic select. HANDLE_TARGET_OPCODE(G_SELECT) diff --git a/llvm/include/llvm/Target/GenericOpcodes.td b/llvm/include/llvm/Target/GenericOpcodes.td index e1710ff2d8abf..f26284e001dbd 100644 --- a/llvm/include/llvm/Target/GenericOpcodes.td +++ b/llvm/include/llvm/Target/GenericOpcodes.td @@ -430,6 +430,20 @@ def G_FCMP : GenericInstruction { let hasSideEffects = false; } +// Generic signed three-way comparison. +def G_SCMP : GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins type1:$src1, type1:$src2); + let hasSideEffects = false; +} + +// Generic unsigned three-way comparison. +def G_UCMP : GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins type1:$src1, type1:$src2); + let hasSideEffects = false; +} + // Generic select def G_SELECT : GenericInstruction { let OutOperandList = (outs type0:$dst); diff --git a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp index 06a6c1f93ef1f..7eb6cd4e0d798 100644 --- a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp +++ b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp @@ -911,6 +911,18 @@ MachineInstrBuilder MachineIRBuilder::buildFCmp(CmpInst::Predicate Pred, return buildInstr(TargetOpcode::G_FCMP, Res, {Pred, Op0, Op1}, Flags); } +MachineInstrBuilder MachineIRBuilder::buildSCmp(const DstOp &Res, + const SrcOp &Op0, + const SrcOp &Op1) { + return buildInstr(TargetOpcode::G_SCMP, Res, {Op0, Op1}); +} + +MachineInstrBuilder MachineIRBuilder::buildUCmp(const DstOp &Res, + const SrcOp &Op0, + const SrcOp &Op1) { + return buildInstr(TargetOpcode::G_UCMP, Res, {Op0, Op1}); +} + MachineInstrBuilder MachineIRBuilder::buildSelect(const DstOp &Res, const SrcOp &Tst, const SrcOp &Op0, const SrcOp &Op1, diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp index 0a5b8bdbc9371..d22fbe322ec36 100644 --- a/llvm/lib/CodeGen/MachineVerifier.cpp +++ b/llvm/lib/CodeGen/MachineVerifier.cpp @@ -1544,6 +1544,36 @@ void MachineVerifier::verifyPreISelGenericInstruction(const MachineInstr *MI) { break; } + case TargetOpcode::G_SCMP: + case TargetOpcode::G_UCMP: { + LLT DstTy = MRI->getType(MI->getOperand(0).getReg()); + LLT SrcTy = MRI->getType(MI->getOperand(1).getReg()); + LLT SrcTy2 = MRI->getType(MI->getOperand(2).getReg()); + + if (SrcTy.isPointerOrPointerVector() || SrcTy2.isPointerOrPointerVector()) { + report("Generic scmp/ucmp does not support pointers as operands", MI); + break; + } + + if (DstTy.isPointerOrPointerVector()) { + report("Generic scmp/ucmp does not support pointers as a result", MI); + break; + } + + if ((DstTy.isVector() != SrcTy.isVector()) || + (DstTy.isVector() && + DstTy.getElementCount() != SrcTy.getElementCount())) { + report("Generic vector scmp/ucmp must preserve number of lanes", MI); + break; + } + + if (SrcTy != SrcTy2) { + report("Generic scmp/ucmp must have same input types", MI); + break; + } + + break; + } case TargetOpcode::G_EXTRACT: { const MachineOperand &SrcOp = MI->getOperand(1); if (!SrcOp.isReg()) { diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir index 6db0b9326ca47..342c8f34d051c 100644 --- a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir +++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir @@ -351,6 +351,12 @@ # DEBUG-NEXT: G_FCMP (opcode {{[0-9]+}}): 2 type indices, 0 imm indices # DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected # DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected +# DEBUG-NEXT: G_SCMP (opcode {{[0-9]+}}): 2 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 +# DEBUG-NEXT: G_UCMP (opcode {{[0-9]+}}): 2 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 # DEBUG-NEXT: G_SELECT (opcode {{[0-9]+}}): 2 type indices, 0 imm indices # DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected # DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected diff --git a/llvm/test/MachineVerifier/test_uscmp.mir b/llvm/test/MachineVerifier/test_uscmp.mir new file mode 100644 index 0000000000000..aa686c4ec73e6 --- /dev/null +++ b/llvm/test/MachineVerifier/test_uscmp.mir @@ -0,0 +1,31 @@ +# RUN: not --crash llc -verify-machineinstrs -run-pass none -mtriple=arm64 -o /dev/null %s 2>&1 | FileCheck %s +# REQUIRES: aarch64-registered-target + +--- +name: test_uscmp +body: | + bb.0: + + %2:_(p0) = G_IMPLICIT_DEF + %3:_(p0) = G_IMPLICIT_DEF + ; CHECK: Generic scmp/ucmp does not support pointers as operands + %4:_(s1) = G_SCMP %2, %3 + + %12:_(s64) = G_IMPLICIT_DEF + %13:_(s64) = G_IMPLICIT_DEF + ; CHECK: Generic scmp/ucmp does not support pointers as a result + %14:_(p0) = G_SCMP %12, %13 + + %23:_(<2 x s32>) = G_IMPLICIT_DEF + %24:_(<2 x s32>) = G_IMPLICIT_DEF + ; CHECK: Generic vector scmp/ucmp must preserve number of lanes + %5:_(s1) = G_UCMP %23, %24 + + %15:_(s32) = G_CONSTANT i32 0 + %16:_(s64) = G_CONSTANT i64 2 + ; CHECK: Generic scmp/ucmp must have same input types + %17:_(s1) = G_SCMP %15, %16 + + + +... diff --git a/llvm/test/TableGen/GlobalISelCombinerEmitter/match-table-cxx.td b/llvm/test/TableGen/GlobalISelCombinerEmitter/match-table-cxx.td index 7bbde818082ce..0dcd0c2dd50a8 100644 --- a/llvm/test/TableGen/GlobalISelCombinerEmitter/match-table-cxx.td +++ b/llvm/test/TableGen/GlobalISelCombinerEmitter/match-table-cxx.td @@ -86,12 +86,12 @@ def MyCombiner: GICombiner<"GenMyCombiner", [ // CHECK: const uint8_t *GenMyCombiner::getMatchTable() const { // CHECK-NEXT: constexpr static uint8_t MatchTable0[] = { // CHECK-NEXT: GIM_SwitchOpcode, /*MI*/0, /*[*/GIMT_Encode2([[#LOWER:]]), GIMT_Encode2([[#UPPER:]]), /*)*//*default:*//*Label 4*/ GIMT_Encode4([[#DEFAULT:]]), -// CHECK-NEXT: /*TargetOpcode::G_STORE*//*Label 0*/ GIMT_Encode4(410), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), -// CHECK-NEXT: /*TargetOpcode::G_SEXT*//*Label 1*/ GIMT_Encode4(428), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), -// CHECK-NEXT: /*TargetOpcode::G_FNEG*//*Label 2*/ GIMT_Encode4(440), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), -// CHECK-NEXT: /*TargetOpcode::G_FABS*//*Label 3*/ GIMT_Encode4(452), +// CHECK-NEXT: /*TargetOpcode::G_STORE*//*Label 0*/ GIMT_Encode4(418), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), +// CHECK-NEXT: /*TargetOpcode::G_SEXT*//*Label 1*/ GIMT_Encode4(436), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), +// CHECK-NEXT: /*TargetOpcode::G_FNEG*//*Label 2*/ GIMT_Encode4(448), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), +// CHECK-NEXT: /*TargetOpcode::G_FABS*//*Label 3*/ GIMT_Encode4(460), // CHECK-NEXT: // Label 0: @[[#%u, mul(UPPER-LOWER, 4) + 10]] -// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 5*/ GIMT_Encode4(427), // Rule ID 2 // +// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 5*/ GIMT_Encode4(435), // Rule ID 2 // // CHECK-NEXT: GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule2Enabled), // CHECK-NEXT: // MIs[0] x // CHECK-NEXT: // No operand predicates @@ -101,10 +101,10 @@ def MyCombiner: GICombiner<"GenMyCombiner", [ // CHECK-NEXT: GIM_CheckCxxInsnPredicate, /*MI*/0, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_GICombiner1), // CHECK-NEXT: // Combiner Rule #2: TwoMatchNoApply // CHECK-NEXT: GIR_EraseRootFromParent_Done, -// CHECK-NEXT: // Label 5: @427 +// CHECK-NEXT: // Label 5: @435 // CHECK-NEXT: GIM_Reject, -// CHECK-NEXT: // Label 1: @428 -// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 6*/ GIMT_Encode4(439), // Rule ID 3 // +// CHECK-NEXT: // Label 1: @436 +// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 6*/ GIMT_Encode4(447), // Rule ID 3 // // CHECK-NEXT: GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule3Enabled), // CHECK-NEXT: // MIs[0] a // CHECK-NEXT: // No operand predicates @@ -112,10 +112,10 @@ def MyCombiner: GICombiner<"GenMyCombiner", [ // CHECK-NEXT: // No operand predicates // CHECK-NEXT: // Combiner Rule #3: NoMatchTwoApply // CHECK-NEXT: GIR_DoneWithCustomAction, /*Fn*/GIMT_Encode2(GICXXCustomAction_GICombiner2), -// CHECK-NEXT: // Label 6: @439 +// CHECK-NEXT: // Label 6: @447 // CHECK-NEXT: GIM_Reject, -// CHECK-NEXT: // Label 2: @440 -// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 7*/ GIMT_Encode4(451), // Rule ID 1 // +// CHECK-NEXT: // Label 2: @448 +// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 7*/ GIMT_Encode4(459), // Rule ID 1 // // CHECK-NEXT: GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule1Enabled), // CHECK-NEXT: // MIs[0] a // CHECK-NEXT: // No operand predicates @@ -123,10 +123,10 @@ def MyCombiner: GICombiner<"GenMyCombiner", [ // CHECK-NEXT: // No operand predicates // CHECK-NEXT: // Combiner Rule #1: TwoMatchTwoApply // CHECK-NEXT: GIR_DoneWithCustomAction, /*Fn*/GIMT_Encode2(GICXXCustomAction_GICombiner1), -// CHECK-NEXT: // Label 7: @451 +// CHECK-NEXT: // Label 7: @459 // CHECK-NEXT: GIM_Reject, -// CHECK-NEXT: // Label 3: @452 -// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 8*/ GIMT_Encode4(463), // Rule ID 0 // +// CHECK-NEXT: // Label 3: @460 +// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 8*/ GIMT_Encode4(471), // Rule ID 0 // // CHECK-NEXT: GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule0Enabled), // CHECK-NEXT: // MIs[0] a // CHECK-NEXT: // No operand predicates @@ -134,7 +134,7 @@ def MyCombiner: GICombiner<"GenMyCombiner", [ // CHECK-NEXT: // No operand predicates // CHECK-NEXT: // Combiner Rule #0: OneMatchOneApply // CHECK-NEXT: GIR_DoneWithCustomAction, /*Fn*/GIMT_Encode2(GICXXCustomAction_GICombiner0), -// CHECK-NEXT: // Label 8: @463 +// CHECK-NEXT: // Label 8: @471 // CHECK-NEXT: GIM_Reject, // CHECK-NEXT: // Label 4: @[[#%u, DEFAULT]] // CHECK-NEXT: GIM_Reject, diff --git a/llvm/test/TableGen/GlobalISelEmitter.td b/llvm/test/TableGen/GlobalISelEmitter.td index 796f595930319..853831366fa53 100644 --- a/llvm/test/TableGen/GlobalISelEmitter.td +++ b/llvm/test/TableGen/GlobalISelEmitter.td @@ -513,7 +513,7 @@ def : Pat<(frag GPR32:$src1, complex:$src2, complex:$src3), // R00O-NEXT: GIM_Reject, // R00O: // Label [[DEFAULT_NUM]]: @[[DEFAULT]] // R00O-NEXT: GIM_Reject, -// R00O-NEXT: }; // Size: 1808 bytes +// R00O-NEXT: }; // Size: 1816 bytes def INSNBOB : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3, GPR32:$src4), [(set GPR32:$dst,