Skip to content

[GlobalIsel] Add G_SCMP and G_UCMP instructions #98894

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 4 commits into from
Jul 18, 2024
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
20 changes: 20 additions & 0 deletions llvm/docs/GlobalISel/GenericOpcode.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
^^^^^^^^

Expand Down
28 changes: 28 additions & 0 deletions llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,34 @@ class MachineIRBuilder {
const SrcOp &Op0, const SrcOp &Op1,
std::optional<unsigned> 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 <N x s2>.
/// \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 <N x s2>.
/// \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) {
Expand Down
6 changes: 6 additions & 0 deletions llvm/include/llvm/Support/TargetOpcodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
14 changes: 14 additions & 0 deletions llvm/include/llvm/Target/GenericOpcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
12 changes: 12 additions & 0 deletions llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
30 changes: 30 additions & 0 deletions llvm/lib/CodeGen/MachineVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Are pointers valid?

Copy link
Author

Choose a reason for hiding this comment

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

I linked the LangRef PR. Langref claims it is integer-only.

break;
}
case TargetOpcode::G_EXTRACT: {
const MachineOperand &SrcOp = MI->getOperand(1);
if (!SrcOp.isReg()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 31 additions & 0 deletions llvm/test/MachineVerifier/test_uscmp.mir
Original file line number Diff line number Diff line change
@@ -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
Copy link
Contributor

Choose a reason for hiding this comment

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

Checking the operands separately from the result would be better


%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



...
30 changes: 15 additions & 15 deletions llvm/test/TableGen/GlobalISelCombinerEmitter/match-table-cxx.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -101,40 +101,40 @@ 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
// CHECK-NEXT: // MIs[0] y
// 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
// CHECK-NEXT: // MIs[0] b
// 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
// CHECK-NEXT: // MIs[0] b
// 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,
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/TableGen/GlobalISelEmitter.td
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading