diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h --- a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -242,6 +242,12 @@ /// - OpIdx - Operand index GIM_CheckIsImm, + /// Check an immediate predicate on the specified operand + /// - InsnID - Instruction ID + /// - OpIdx - Operand index + /// - The predicate to test + GIM_CheckI64ImmArgPredicate, + /// Check if the specified operand is safe to fold into the current /// instruction. /// - InsnID - Instruction ID diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h --- a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h @@ -779,6 +779,26 @@ } break; } + case GIM_CheckI64ImmArgPredicate: { + int64_t InsnID = MatchTable[CurrentIdx++]; + int64_t OpIdx = MatchTable[CurrentIdx++]; + int64_t Predicate = MatchTable[CurrentIdx++]; + DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), + dbgs() + << CurrentIdx << ": GIM_CheckI64ImmArgPredicate(MIs[" + << InsnID << "]->getOperand(" << OpIdx + << "), Predicate=" << Predicate << ")\n"); + assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); + assert(State.MIs[InsnID]->getOperand(OpIdx).isImm() && + "Expected an immediate operand"); + assert(Predicate > GIPFP_I64_Invalid && "Expected a valid predicate"); + + int64_t Value = State.MIs[InsnID]->getOperand(OpIdx).getImm(); + if (!testImmPredicate_I64(Predicate, Value)) + if (handleReject() == RejectAndGiveUp) + return false; + break; + } case GIM_CheckIsSafeToFold: { int64_t InsnID = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), diff --git a/llvm/test/TableGen/immarg-predicated.td b/llvm/test/TableGen/immarg-predicated.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/immarg-predicated.td @@ -0,0 +1,22 @@ +// RUN: llvm-tblgen -gen-global-isel -optimize-match-table=false -I %p/Common -I %p/../../include %s -o - < %s | FileCheck -check-prefix=GISEL %s + +include "llvm/Target/Target.td" +include "GlobalISelEmitterCommon.td" + +let TargetPrefix = "mytarget" in { +def int_mytarget_sleep0 : Intrinsic<[], [llvm_i32_ty], [ImmArg>]>; +} + +// GISEL: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS, +// GISEL-NEXT: // MIs[0] Operand 0 +// GISEL-NEXT: GIM_CheckIntrinsicID, /*MI*/0, /*Op*/0, Intrinsic::mytarget_sleep0, +// GISEL-NEXT: // MIs[0] src +// GISEL-NEXT: GIM_CheckIsImm, /*MI*/0, /*Op*/1, +// GISEL-NEXT: GIM_CheckI64ImmArgPredicate, /*MI*/0, /*MO*/1, /*Predicate*/GIPFP_I64_Predicate_tuimm9, +// GISEL-NEXT: // (intrinsic_void {{[0-9]+}}:{ *:[iPTR] }, (timm:{ *:[i32] })<>:$src) => (SLEEP0 (timm:{ *:[i32] }):$src) +// GISEL-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::SLEEP0, +// GISEL-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src +def tuimm9 : TImmLeaf(Imm); }]>; +def SLEEP0 : I<(outs), (ins i32imm:$src), + [(int_mytarget_sleep0 tuimm9:$src)] +>; diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -94,6 +94,12 @@ return "GIM_Check" + Predicate.getImmTypeIdentifier().str() + "ImmPredicate"; } +/// Same as getMatchOpcodeForPredicate but for immediate operands +std::string getMatchOpcodeForImmArgPredicate(const TreePredicateFn &Predicate) { + return "GIM_Check" + Predicate.getImmTypeIdentifier().str() + + "ImmArgPredicate"; +} + /// This class stands in for LLT wherever we want to tablegen-erate an /// equivalent at compiler run-time. class LLTCodeGen { @@ -1562,6 +1568,40 @@ } }; +/// Generates code to check that this operand is an immediate whose value meets +/// an immediate predicate. +class OperandImmPredicateMatcher : public OperandPredicateMatcher { +protected: + TreePredicateFn Predicate; + +public: + OperandImmPredicateMatcher(unsigned InsnVarID, unsigned OpIdx, + const TreePredicateFn &Predicate) + : OperandPredicateMatcher(IPM_ImmPredicate, InsnVarID, OpIdx), + Predicate(Predicate) {} + + bool isIdentical(const PredicateMatcher &B) const override { + return OperandPredicateMatcher::isIdentical(B) && + Predicate.getOrigPatFragRecord() == + cast(&B) + ->Predicate.getOrigPatFragRecord(); + } + + static bool classof(const PredicateMatcher *P) { + return P->getKind() == IPM_ImmPredicate; + } + + void emitPredicateOpcodes(MatchTable &Table, + RuleMatcher &Rule) const override { + Table << MatchTable::Opcode(getMatchOpcodeForImmArgPredicate(Predicate)) + << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) + << MatchTable::Comment("MO") << MatchTable::IntValue(OpIdx) + << MatchTable::Comment("Predicate") + << MatchTable::NamedValue(getEnumNameForPredicate(Predicate)) + << MatchTable::LineBreak; + } +}; + /// Generates code to check that a set of predicates match for a particular /// operand. class OperandMatcher : public PredicateListMatcher { @@ -4153,6 +4193,17 @@ } if (SrcChild->getOperator()->getName() == "timm") { OM.addPredicate(); + + // Add predicates, if any + for (const TreePredicateCall &Call : SrcChild->getPredicateCalls()) { + const TreePredicateFn &Predicate = Call.Fn; + + // Only handle immediate patterns for now + if (Predicate.isImmediatePattern()) { + OM.addPredicate(Predicate); + } + } + return Error::success(); } }