diff --git a/llvm/docs/GlobalISel/MIRPatterns.rst b/llvm/docs/GlobalISel/MIRPatterns.rst --- a/llvm/docs/GlobalISel/MIRPatterns.rst +++ b/llvm/docs/GlobalISel/MIRPatterns.rst @@ -141,6 +141,35 @@ * The root cannot have any output operands. * The root must be a CodeGenInstruction +Instruction Flags +----------------- + +MIR Patterns support both matching & writing ``MIFlags``. +``MIFlags`` are never preserved; output instructions have never have +any flags unless explicitly set. + +.. code-block:: text + :caption: Example + + # MIFlags supports multiple flags. + # MIFlag is a shorthand version for a single flag. + def Test : GICombineRule< + (defs root:$dst), + (match (G_ZEXT $dst, $src, MIFlags<[FmNoNans, FmNoInfs]>)), + (apply (G_SEXT $dst, $src, MIFlag))>; + +``MIFlags`` operands are not actual instruction operands - they just tell +the backend that the pattern being matched/written has some flags. + +``MIFlags`` may appear multiple times, and anywhere in a +CodeGenInstruction pattern, but for readability, please try to keep the style +consistent. e.g. If they're always at the end of the pattern in the current +file, keep it that way. Similarly, if the current file prefers using one +``MIFlags`` instead of multiple ``MIFlag`` to describe multiple flags, +keep it that way as well. + +Note that flags are uniqued for the whole pattern, so even if a given flag is +mentioned multiple times, it's only checked/added once. Limitations ----------- diff --git a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h --- a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h @@ -265,6 +265,13 @@ /// - NewOpIdx GIM_CheckCanReplaceReg, + /// Checks for the presence of a MIFlag. + /// Supports checking for multiple OR'd flags at once + /// (then all of them must be present) + /// - InsnID - Instruction ID to check. + /// - Flag - Flag(s) to check. + GIM_CheckHasMIFlag, + /// Predicates with 'let PredicateCodeUsesOperands = 1' need to examine some /// named operands that will be recorded in RecordedOperands. Names of these /// operands are referenced in predicate argument list. Emitter determines @@ -332,6 +339,13 @@ /// OpIdx starts at 0 for the first implicit def. GIR_SetImplicitDefDead, + /// Sets MIFlags of a given instruction. + /// Supports add multiple OR'd flags at once. + /// Note: Overwrites all previously set flags on the instruction! + /// - InsnID - Instruction ID to modify. + /// - Flag - Flag(s) to add. + GIR_SetMIFlags, + /// Add a temporary register to the specified instruction /// - InsnID - Instruction ID to modify /// - TempRegID - The temporary register ID to add @@ -549,7 +563,9 @@ }; protected: - GIMatchTableExecutor(); + const bool PropagateMIFlags; + + GIMatchTableExecutor(bool PropagateMIFlags); /// Execute a given matcher table and return true if the match was successful /// and false otherwise. diff --git a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h --- a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h @@ -80,8 +80,6 @@ MIBFlags |= MachineInstr::NoFPExcept; MIB.setMIFlags(MIBFlags); } - - return true; }; while (true) { @@ -885,6 +883,23 @@ } break; } + case GIM_CheckHasMIFlag: { + int64_t InsnID = MatchTable[CurrentIdx++]; + int64_t Flags = MatchTable[CurrentIdx++]; + + DEBUG_WITH_TYPE(TgtExecutor::getName(), + dbgs() + << CurrentIdx << ": GIM_CheckHasMIFlag(MIs[" << InsnID + << "], Flags=" << (uint64_t)Flags << ")\n"); + + // Need to support multiple OR'd flags, so we can't just do a simple AND. + const int64_t MIFlags = State.MIs[InsnID]->getFlags(); + if ((MIFlags & Flags) != Flags) { + if (handleReject() == RejectAndGiveUp) + return false; + } + break; + } case GIM_Reject: DEBUG_WITH_TYPE(TgtExecutor::getName(), dbgs() << CurrentIdx << ": GIM_Reject\n"); @@ -1015,6 +1030,19 @@ MI->getOperand(MI->getNumExplicitOperands() + OpIdx).setIsDead(); break; } + case GIR_SetMIFlags: { + int64_t InsnID = MatchTable[CurrentIdx++]; + int64_t Flags = MatchTable[CurrentIdx++]; + DEBUG_WITH_TYPE(TgtExecutor::getName(), + dbgs() << CurrentIdx << ": GIR_SetMIFlags(OutMIs[" + << InsnID << "], Flags=" << Flags << ")\n"); + auto &MI = OutMIs[InsnID]; + // TODO: Should we really clear MIFlags here? It's to avoid MutateOpcode + // keeping old flags around. + MI->clearFlag(MachineInstr::MIFlag(~uint32_t(0))); + MI.setMIFlags(Flags); + break; + } case GIR_AddTempRegister: case GIR_AddTempSubRegister: { int64_t InsnID = MatchTable[CurrentIdx++]; @@ -1313,7 +1341,8 @@ for (MachineInstr *MI : OutMIs) Observer->createdInstr(*MI); } - propagateFlags(OutMIs); + if (PropagateMIFlags) + propagateFlags(OutMIs); return true; default: llvm_unreachable("Unexpected command"); 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 @@ -18,6 +18,7 @@ namespace llvm { class InstructionSelector : public GIMatchTableExecutor { public: + InstructionSelector() : GIMatchTableExecutor(/*PropagateMIFlags*/ true) {} virtual ~InstructionSelector(); /// Select the (possibly generic) instruction \p I to only use target-specific diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td --- a/llvm/include/llvm/Target/GlobalISel/Combine.td +++ b/llvm/include/llvm/Target/GlobalISel/Combine.td @@ -110,6 +110,28 @@ list Alternatives = alts; } +//===----------------------------------------------------------------------===// +// Pattern MIFlags +//===----------------------------------------------------------------------===// + +class MIFlagEnum { + string EnumName = "MachineInstr::" # enumName; +} + +def FmNoNans : MIFlagEnum<"FmNoNans">; +def FmNoInfs : MIFlagEnum<"FmNoInfs">; +def FmNsz : MIFlagEnum<"FmNsz">; +def FmArcp : MIFlagEnum<"FmArcp">; +def FmContract : MIFlagEnum<"FmContract">; +def FmAfn : MIFlagEnum<"FmAfn">; +def FmReassoc : MIFlagEnum<"FmReassoc">; + +class MIFlags flags> { + list Flags = flags; +} + +class MIFlag : MIFlags<[flag]>; + //===----------------------------------------------------------------------===// // Pattern Builtins //===----------------------------------------------------------------------===// diff --git a/llvm/lib/CodeGen/GlobalISel/Combiner.cpp b/llvm/lib/CodeGen/GlobalISel/Combiner.cpp --- a/llvm/lib/CodeGen/GlobalISel/Combiner.cpp +++ b/llvm/lib/CodeGen/GlobalISel/Combiner.cpp @@ -91,7 +91,8 @@ Combiner::Combiner(MachineFunction &MF, CombinerInfo &CInfo, const TargetPassConfig *TPC, GISelKnownBits *KB, GISelCSEInfo *CSEInfo) - : Builder(CSEInfo ? std::make_unique() + : GIMatchTableExecutor(/*PropagateMIFlags=*/false), + Builder(CSEInfo ? std::make_unique() : std::make_unique()), WLObserver(std::make_unique(WorkList)), ObserverWrapper(std::make_unique()), CInfo(CInfo), diff --git a/llvm/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp b/llvm/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp --- a/llvm/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp +++ b/llvm/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp @@ -24,7 +24,8 @@ GIMatchTableExecutor::MatcherState::MatcherState(unsigned MaxRenderers) : Renderers(MaxRenderers) {} -GIMatchTableExecutor::GIMatchTableExecutor() = default; +GIMatchTableExecutor::GIMatchTableExecutor(bool PropagateMIFlags) + : PropagateMIFlags(PropagateMIFlags) {} bool GIMatchTableExecutor::isOperandImmEqual(const MachineOperand &MO, int64_t Value, diff --git a/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/builtin-pattern-errors.td b/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/builtin-pattern-errors.td --- a/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/builtin-pattern-errors.td +++ b/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/builtin-pattern-errors.td @@ -78,6 +78,13 @@ (match (COPY $dst, $tmp), (COPY $tmp, $src)), (apply (GIReplaceReg $dst, $src), (GIReplaceReg $tmp, $src))>; +// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: MIFlags operands are only allowed on CodeGenInstruction patterns +// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(GIEraseRoot anonymous_ +def miflags_notinst : GICombineRule< + (defs root:$mi), + (match (G_STORE $a, $b):$mi), + (apply (GIEraseRoot MIFlag))>; + // CHECK: error: Failed to parse one or more rules def MyCombiner: GICombiner<"GenMyCombiner", [ @@ -90,5 +97,6 @@ eraseroot_notinstmatch, replacereg_in_match, replacereg_ops, - replacereg_nonroot + replacereg_nonroot, + miflags_notinst ]>; diff --git a/llvm/test/TableGen/GlobalISelCombinerEmitter/matchtable-miflags.td b/llvm/test/TableGen/GlobalISelCombinerEmitter/matchtable-miflags.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/GlobalISelCombinerEmitter/matchtable-miflags.td @@ -0,0 +1,63 @@ +// RUN: llvm-tblgen -I %p/../../../include -gen-global-isel-combiner \ +// RUN: -combiners=MyCombiner %s | \ +// RUN: FileCheck %s + +include "llvm/Target/Target.td" +include "llvm/Target/GlobalISel/Combine.td" + +def MyTargetISA : InstrInfo; +def MyTarget : Target { let InstructionSet = MyTargetISA; } + +def TestMutate : GICombineRule< + (defs root:$dst), + (match (G_ZEXT $dst, $src, MIFlag, MIFlags<[FmNoNans, FmNoInfs]>)), + (apply (G_SEXT $dst, $src, MIFlag, MIFlags<[FmReassoc, FmAfn]>))>; + +def TestNewInst : GICombineRule< + (defs root:$dst), + (match (G_ZEXT $dst, $src, MIFlag, MIFlags<[FmAfn, FmReassoc]>)), + (apply (G_FADD $dst, $src, $src, MIFlag, MIFlags<[FmNoNans, FmAfn]>))>; + +// CHECK: const int64_t *GenMyCombiner::getMatchTable() const { +// CHECK-NEXT: constexpr static int64_t MatchTable0[] = { +// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 0*/ 49, +// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_ZEXT, +// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 1*/ 20, // Rule ID 0 // +// CHECK-NEXT: GIM_CheckSimplePredicate, GICXXPred_Simple_IsRule0Enabled, +// CHECK-NEXT: GIM_CheckHasMIFlag, /*MI*/0, MachineInstr::FmNoInfs | MachineInstr::FmNoNans, +// CHECK-NEXT: // MIs[0] dst +// CHECK-NEXT: // No operand predicates +// CHECK-NEXT: // MIs[0] src +// CHECK-NEXT: // No operand predicates +// CHECK-NEXT: // Combiner Rule #0: TestMutate +// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/TargetOpcode::G_SEXT, +// CHECK-NEXT: GIR_SetMIFlags, /*InsnID*/0, MachineInstr::FmReassoc | MachineInstr::FmAfn, +// CHECK-NEXT: GIR_Done, +// CHECK-NEXT: // Label 1: @20 +// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 2*/ 48, // Rule ID 1 // +// CHECK-NEXT: GIM_CheckSimplePredicate, GICXXPred_Simple_IsRule1Enabled, +// CHECK-NEXT: GIM_CheckHasMIFlag, /*MI*/0, MachineInstr::FmAfn | MachineInstr::FmReassoc, +// CHECK-NEXT: // MIs[0] dst +// CHECK-NEXT: // No operand predicates +// CHECK-NEXT: // MIs[0] src +// CHECK-NEXT: // No operand predicates +// CHECK-NEXT: // Combiner Rule #1: TestNewInst +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/TargetOpcode::G_FADD, +// CHECK-NEXT: GIR_SetMIFlags, /*InsnID*/0, MachineInstr::FmNoNans | MachineInstr::FmAfn, +// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst +// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src +// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src +// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0, +// CHECK-NEXT: GIR_Done, +// CHECK-NEXT: // Label 2: @48 +// CHECK-NEXT: GIM_Reject, +// CHECK-NEXT: // Label 0: @49 +// CHECK-NEXT: GIM_Reject, +// CHECK-NEXT: }; +// CHECK-NEXT: return MatchTable0; +// CHECK-NEXT: } + +def MyCombiner: GICombiner<"GenMyCombiner", [ + TestMutate, + TestNewInst +]>; diff --git a/llvm/test/TableGen/GlobalISelCombinerEmitter/patfrag-errors.td b/llvm/test/TableGen/GlobalISelCombinerEmitter/patfrag-errors.td --- a/llvm/test/TableGen/GlobalISelCombinerEmitter/patfrag-errors.td +++ b/llvm/test/TableGen/GlobalISelCombinerEmitter/patfrag-errors.td @@ -271,6 +271,13 @@ (match (RootDefHasMultiDefs $root, (i32 10))), (apply (COPY $root, (i32 0)))>; +// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: MIFlags operands are only allowed on CodeGenInstruction patterns +// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(DummyPF anonymous_ +def miflags_notinst : GICombineRule< + (defs root:$root), + (match (COPY $root, $src), (DummyPF MIFlag)), + (apply (COPY $root, (i32 0)))>; + // CHECK: error: Failed to parse one or more rules def MyCombiner: GICombiner<"GenMyCombiner", [ @@ -293,5 +300,6 @@ patfrag_in_apply, patfrag_cannot_be_root, inconsistent_arg_type, - root_def_has_multi_defs + root_def_has_multi_defs, + miflags_notinst ]>; diff --git a/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-errors.td b/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-errors.td --- a/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-errors.td +++ b/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-errors.td @@ -217,6 +217,13 @@ (apply (COPY i32:$tmp, $y), (COPY $x, (i32 0):$tmp):$foo)>; +// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: MIFlags operands cannot be named +// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(COPY ?:$x, ?:$y, anonymous_6988:$tmp)' +def miflags_named : GICombineRule< + (defs root:$x), + (match (G_FNEG $x, $y)), + (apply (COPY $x, $y, MIFlag:$tmp))>; + // CHECK: error: Failed to parse one or more rules def MyCombiner: GICombiner<"GenMyCombiner", [ @@ -251,5 +258,6 @@ bad_mo_type_not_a_valuetype, untyped_new_reg_in_apply, def_named_imm_match, - def_named_imm_apply + def_named_imm_apply, + miflags_named ]>; diff --git a/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-parsing.td b/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-parsing.td --- a/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-parsing.td +++ b/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-parsing.td @@ -297,6 +297,26 @@ (apply (COPY $a, (i32 0)), (COPY $b, (i32 0)))>; +// CHECK: (CombineRule name:MIFlagsTest id:10 root:dst +// CHECK-NEXT: (MatchPats +// CHECK-NEXT: __MIFlagsTest_match_0:(CodeGenInstructionPattern G_ZEXT operands:[$dst, $src] flags:[MachineInstr::FmNoInfs, MachineInstr::FmNoNans]) +// CHECK-NEXT: ) +// CHECK-NEXT: (ApplyPats +// CHECK-NEXT: __MIFlagsTest_apply_0:(CodeGenInstructionPattern G_ZEXT operands:[$dst, $src] flags:[MachineInstr::FmReassoc, MachineInstr::FmAfn]) +// CHECK-NEXT: ) +// CHECK-NEXT: (OperandTable MatchPats +// CHECK-NEXT: dst -> __MIFlagsTest_match_0 +// CHECK-NEXT: src -> +// CHECK-NEXT: ) +// CHECK-NEXT: (OperandTable ApplyPats +// CHECK-NEXT: dst -> __MIFlagsTest_apply_0 +// CHECK-NEXT: src -> +// CHECK-NEXT: ) +// CHECK-NEXT: ) +def MIFlagsTest : GICombineRule< + (defs root:$dst), + (match (G_ZEXT $dst, $src, MIFlag, MIFlags<[FmNoNans, FmNoInfs]>)), + (apply (G_ZEXT $dst, $src, MIFlag, MIFlags<[FmReassoc, FmAfn]>))>; def MyCombiner: GICombiner<"GenMyCombiner", [ WipOpcodeTest0, @@ -308,5 +328,6 @@ PatFragTest0, PatFragTest1, VariadicsInTest, - VariadicsOutTest + VariadicsOutTest, + MIFlagsTest ]>; diff --git a/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp b/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp --- a/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp @@ -73,6 +73,8 @@ constexpr StringLiteral CXXPredPrefix = "GICXXPred_MI_Predicate_"; constexpr StringLiteral PatFragClassName = "GICombinePatFrag"; constexpr StringLiteral BuiltinInstClassName = "GIBuiltinInst"; +constexpr StringLiteral MIFlagsEnumClassName = "MIFlagEnum"; +constexpr StringLiteral MIFlagsClassName = "MIFlags"; std::string getIsEnabledPredicateEnumName(unsigned CombinerRuleID) { return "GICXXPred_Simple_IsRule" + to_string(CombinerRuleID) + "Enabled"; @@ -630,6 +632,8 @@ protected: InstructionPattern(unsigned K, StringRef Name) : Pattern(K, Name) {} + virtual void printExtras(raw_ostream &OS) const {} + SmallVector Operands; }; @@ -673,6 +677,8 @@ Sep = ", "; } OS << "]"; + + printExtras(OS); }); } @@ -783,11 +789,21 @@ unsigned getNumInstDefs() const override; unsigned getNumInstOperands() const override; + bool hasMIFlags() const { return !MIFlags.empty(); } + void addMIFlag(StringRef Flag) { + if (find(MIFlags, Flag) == MIFlags.end()) + MIFlags.push_back(Flag); + } + const auto &mi_flags() const { return MIFlags; } + const CodeGenInstruction &getInst() const { return I; } StringRef getInstName() const override { return I.TheDef->getName(); } private: + void printExtras(raw_ostream &OS) const override; + const CodeGenInstruction &I; + std::vector MIFlags; }; bool CodeGenInstructionPattern::hasVariadicDefs() const { @@ -821,6 +837,13 @@ : NumCGIOps; } +void CodeGenInstructionPattern::printExtras(raw_ostream &OS) const { + if (MIFlags.empty()) + return; + + OS << " flags:[" << join(MIFlags, ", ") << ']'; +} + //===- OperandTypeChecker -------------------------------------------------===// /// This is a trivial type checker for all operands in a set of @@ -1604,6 +1627,8 @@ bool parseInstructionPatternOperand(InstructionPattern &IP, const Init *OpInit, const StringInit *OpName) const; + bool parseInstructionPatternMIFlagsOperand(InstructionPattern &IP, + const Record *Flag) const; std::unique_ptr parsePatFragImpl(const Record *Def) const; bool parsePatFragParamList( ArrayRef DiagLoc, const DagInit &OpsList, @@ -2400,6 +2425,22 @@ } for (unsigned K = 0; K < DagPat->getNumArgs(); ++K) { + Init *Arg = DagPat->getArg(K); + StringInit *Name = DagPat->getArgName(K); + + if (auto *Def = dyn_cast(Arg); + Def && Def->getDef()->isSubClassOf(MIFlagsClassName)) { + if (!parseInstructionPatternMIFlagsOperand(*Pat, Def->getDef())) + return nullptr; + + if (Name) { + PrintError("" + MIFlagsClassName + " operands cannot be named"); + return nullptr; + } + + continue; + } + if (!parseInstructionPatternOperand(*Pat, DagPat->getArg(K), DagPat->getArgName(K))) return nullptr; @@ -2510,6 +2551,27 @@ return ParseErr(); } +bool CombineRuleBuilder::parseInstructionPatternMIFlagsOperand( + InstructionPattern &IP, const Record *OpDef) const { + assert(OpDef->isSubClassOf(MIFlagsClassName)); + + auto *CGIP = dyn_cast(&IP); + if (!CGIP) { + PrintError(MIFlagsClassName + + " operands are only allowed on CodeGenInstruction patterns"); + return false; + } + + const auto Flags = OpDef->getValueAsListOfDefs("Flags"); + for (auto *Flag : Flags) { + // TableGen file should ensure this is correct. + assert(Flag->isSubClassOf(MIFlagsEnumClassName)); + CGIP->addMIFlag(Flag->getValueAsString("EnumName")); + } + + return true; +} + std::unique_ptr CombineRuleBuilder::parsePatFragImpl(const Record *Def) const { auto StackTrace = PrettyStackTraceParse(*Def); @@ -2981,7 +3043,8 @@ // Now render this inst. auto &DstMI = - M.addAction(M.allocateOutputInsnID(), &CGIP.getInst()); + M.addAction(M.allocateOutputInsnID(), &CGIP.getInst(), + std::vector(CGIP.mi_flags())); for (auto &Op : P.operands()) { if (Op.isNamedImmediate()) { @@ -3193,6 +3256,9 @@ IM.addPredicate(&P.getInst()); declareInstExpansion(CE, IM, P.getName()); + if (P.hasMIFlags()) + IM.addPredicate(P.mi_flags()); + for (const auto &[Idx, OriginalO] : enumerate(P.operands())) { // Remap the operand. This is used when emitting InstructionPatterns inside // PatFrags, so it can remap them to the arguments passed to the pattern. diff --git a/llvm/utils/TableGen/GlobalISelMatchTable.h b/llvm/utils/TableGen/GlobalISelMatchTable.h --- a/llvm/utils/TableGen/GlobalISelMatchTable.h +++ b/llvm/utils/TableGen/GlobalISelMatchTable.h @@ -750,6 +750,7 @@ IPM_VectorSplatImm, IPM_NoUse, IPM_GenericPredicate, + IPM_MIFlags, OPM_SameOperand, OPM_ComplexPattern, OPM_IntrinsicID, @@ -1556,6 +1557,26 @@ RuleMatcher &Rule) const override; }; +class MIFlagsInstructionPredicateMatcher : public InstructionPredicateMatcher { + std::vector Flags; + +public: + MIFlagsInstructionPredicateMatcher(unsigned InsnVarID, + std::vector FlagsToCheck) + : InstructionPredicateMatcher(IPM_MIFlags, InsnVarID), + Flags(FlagsToCheck) { + sort(Flags); + } + + static bool classof(const InstructionPredicateMatcher *P) { + return P->getKind() == IPM_MIFlags; + } + + bool isIdentical(const PredicateMatcher &B) const override; + void emitPredicateOpcodes(MatchTable &Table, + RuleMatcher &Rule) const override; +}; + /// Generates code to check for the absence of use of the result. // TODO? Generalize this to support checking for one use. class NoUsePredicateMatcher : public InstructionPredicateMatcher { @@ -2159,13 +2180,16 @@ InstructionMatcher *Matched; std::vector> OperandRenderers; SmallPtrSet DeadImplicitDefs; + std::vector Flags; /// True if the instruction can be built solely by mutating the opcode. bool canMutate(RuleMatcher &Rule, const InstructionMatcher *Insn) const; public: - BuildMIAction(unsigned InsnID, const CodeGenInstruction *I) - : MatchAction(AK_BuildMI), InsnID(InsnID), I(I), Matched(nullptr) {} + BuildMIAction(unsigned InsnID, const CodeGenInstruction *I, + const std::vector &MIFlags = {}) + : MatchAction(AK_BuildMI), InsnID(InsnID), I(I), Matched(nullptr), + Flags(MIFlags) {} static bool classof(const MatchAction *A) { return A->getKind() == AK_BuildMI; diff --git a/llvm/utils/TableGen/GlobalISelMatchTable.cpp b/llvm/utils/TableGen/GlobalISelMatchTable.cpp --- a/llvm/utils/TableGen/GlobalISelMatchTable.cpp +++ b/llvm/utils/TableGen/GlobalISelMatchTable.cpp @@ -1509,6 +1509,21 @@ << MatchTable::LineBreak; } +//===- MIFlagsInstructionPredicateMatcher ---------------------------------===// + +bool MIFlagsInstructionPredicateMatcher::isIdentical( + const PredicateMatcher &B) const { + return InstructionPredicateMatcher::isIdentical(B) && + Flags == + static_cast(B).Flags; +} +void MIFlagsInstructionPredicateMatcher::emitPredicateOpcodes( + MatchTable &Table, RuleMatcher &Rule) const { + Table << MatchTable::Opcode("GIM_CheckHasMIFlag") << MatchTable::Comment("MI") + << MatchTable::IntValue(InsnVarID) + << MatchTable::NamedValue(join(Flags, " | ")) << MatchTable::LineBreak; +} + //===- InstructionMatcher -------------------------------------------------===// OperandMatcher & @@ -1924,6 +1939,17 @@ void BuildMIAction::emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const { + + const auto AddMIFlags = [&]() { + if (Flags.empty()) + return; + + Table << MatchTable::Opcode("GIR_SetMIFlags") + << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID) + << MatchTable::NamedValue(join(Flags, " | ")) + << MatchTable::LineBreak; + }; + if (Matched) { assert(canMutate(Rule, Matched) && "Arranged to mutate an insn that isn't mutatable"); @@ -1937,6 +1963,10 @@ << MatchTable::NamedValue(I->Namespace, I->TheDef->getName()) << MatchTable::LineBreak; + // GIR_SetMIFlags clears flags so we don't need to worry about pre-existing + // ones. + AddMIFlags(); + if (!I->ImplicitDefs.empty() || !I->ImplicitUses.empty()) { for (auto *Def : I->ImplicitDefs) { auto Namespace = Def->getValue("Namespace") @@ -1970,6 +2000,9 @@ << MatchTable::IntValue(InsnID) << MatchTable::Comment("Opcode") << MatchTable::NamedValue(I->Namespace, I->TheDef->getName()) << MatchTable::LineBreak; + + AddMIFlags(); + for (const auto &Renderer : OperandRenderers) Renderer->emitRenderOpcodes(Table, Rule);