From: Daniel Sanders Date: Fri, 15 Jun 2018 23:13:43 +0000 (+0000) Subject: [globalisel][tablegen] Add support for C++ predicates on PatFrags and use it to suppo... X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8ead1290e687b27bb7bd877b6b1768f6579d84b7;p=platform%2Fupstream%2Fllvm.git [globalisel][tablegen] Add support for C++ predicates on PatFrags and use it to support BFC on ARM. So far, we've only handled special cases of PatFrag like ImmLeaf. This patch adds support for the remaining cases using similar mechanisms. Like most C++ code from SelectionDAG, GISel and DAGISel expect to operate on different types and representations and as such the code is not compatible between the two. It's therefore necessary to add an alternative implementation in the GISelPredicateCode field. The target test for this feature could easily be done with IntImmLeaf and this would save on a little boilerplate. The reason I've chosen to implement this using PatFrag.GISelPredicateCode and not IntImmLeaf is because I was unable to find a rule that was blocked solely by lack of support for PatFrag predicates. I found that the ones I investigated as being likely candidates for the test were further blocked by other things. llvm-svn: 334871 --- diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h index a44fd15..471def7 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -147,6 +147,10 @@ enum { GIM_CheckMemorySizeEqualToLLT, GIM_CheckMemorySizeLessThanLLT, GIM_CheckMemorySizeGreaterThanLLT, + /// Check a generic C++ instruction predicate + /// - InsnID - Instruction ID + /// - PredicateID - The ID of the predicate function to call + GIM_CheckCxxInsnPredicate, /// Check the type for the specified operand /// - InsnID - Instruction ID @@ -413,13 +417,20 @@ protected: } virtual bool testImmPredicate_I64(unsigned, int64_t) const { - llvm_unreachable("Subclasses must override this to use tablegen"); + llvm_unreachable( + "Subclasses must override this with a tablegen-erated function"); } virtual bool testImmPredicate_APInt(unsigned, const APInt &) const { - llvm_unreachable("Subclasses must override this to use tablegen"); + llvm_unreachable( + "Subclasses must override this with a tablegen-erated function"); } virtual bool testImmPredicate_APFloat(unsigned, const APFloat &) const { - llvm_unreachable("Subclasses must override this to use tablegen"); + llvm_unreachable( + "Subclasses must override this with a tablegen-erated function"); + } + virtual bool testMIPredicate_MI(unsigned, const MachineInstr &) const { + llvm_unreachable( + "Subclasses must override this with a tablegen-erated function"); } /// Constrain a register operand of an instruction \p I to a specified diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h index 525fb68..2003a79 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h @@ -41,6 +41,7 @@ enum { GIPFP_I64_Invalid = 0, GIPFP_APInt_Invalid = 0, GIPFP_APFloat_Invalid = 0, + GIPFP_MI_Invalid = 0, }; template GIPFP_MI_Invalid && "Expected a valid predicate"); + + if (!testMIPredicate_MI(Predicate, *State.MIs[InsnID])) + if (handleReject() == RejectAndGiveUp) + return false; + break; + } case GIM_CheckAtomicOrdering: { int64_t InsnID = MatchTable[CurrentIdx++]; AtomicOrdering Ordering = (AtomicOrdering)MatchTable[CurrentIdx++]; diff --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td index 7ba8f7e..290a267 100644 --- a/llvm/include/llvm/Target/TargetSelectionDAG.td +++ b/llvm/include/llvm/Target/TargetSelectionDAG.td @@ -623,6 +623,7 @@ class PatFrag, let PrintMethod = "printBitfieldInvMaskImmOperand"; let DecoderMethod = "DecodeBitfieldMaskOperand"; let ParserMatchClass = BitfieldAsmOperand; + let GISelPredicateCode = [{ + // There's better methods of implementing this check. IntImmLeaf<> would be + // equivalent and have less boilerplate but we need a test for C++ + // predicates and this one causes new rules to be imported into GlobalISel + // without requiring additional features first. + const auto &MO = MI.getOperand(1); + if (!MO.isCImm()) + return false; + return ARM::isBitFieldInvertedMask(MO.getCImm()->getZExtValue()); + }]; } def imm1_32_XFORM: SDNodeXFormhasC()"> { let RecomputePerFunction = 1; } // CHECK-NEXT: bool testImmPredicate_APInt(unsigned PredicateID, const APInt &Imm) const override; // CHECK-NEXT: bool testImmPredicate_APFloat(unsigned PredicateID, const APFloat &Imm) const override; // CHECK-NEXT: const int64_t *getMatchTable() const override; +// CHECK-NEXT: bool testMIPredicate_MI(unsigned PredicateID, const MachineInstr &MI) const override; // CHECK-NEXT: #endif // ifdef GET_GLOBALISEL_TEMPORARIES_DECL // CHECK-LABEL: #ifdef GET_GLOBALISEL_TEMPORARIES_INIT @@ -256,11 +257,11 @@ def HasC : Predicate<"Subtarget->hasC()"> { let RecomputePerFunction = 1; } // R19O-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s32, // R19O-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32, // R19O-NEXT: GIM_CheckType, /*MI*/0, /*Op*/3, /*Type*/GILLT_s32, -// R19O-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID, -// R19O-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID, // // R19C-NEXT: GIM_Try, /*On fail goto*//*Label [[LABEL_NUM:[0-9]+]]*/ [[LABEL:[0-9]+]], // +// R19O-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID, +// R19O-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID, // R19N-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/4, // R19N-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_SELECT, // R19N-NEXT: // MIs[0] dst @@ -345,8 +346,6 @@ def : Pat<(select GPR32:$src1, (complex_rr GPR32:$src2a, GPR32:$src2b), // R21O-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s32, // R21O-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32, // R21O-NEXT: GIM_CheckType, /*MI*/0, /*Op*/3, /*Type*/GILLT_s32, -// R21O-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID, -// R21O-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID, // // R21C-NEXT: GIM_Try, /*On fail goto*//*Label [[PREV_NUM:[0-9]+]]*/ [[PREV:[0-9]+]], // Rule ID 19 // // R21C-NOT: GIR_Done, @@ -355,8 +354,12 @@ def : Pat<(select GPR32:$src1, (complex_rr GPR32:$src2a, GPR32:$src2b), // R21C-NEXT: // Label [[PREV_NUM]]: @[[PREV]] // R21C-NEXT: GIM_Try, /*On fail goto*//*Label [[LABEL_NUM:[0-9]+]]*/ [[LABEL:[0-9]+]], // Rule ID 21 // // +// R21O-NEXT: GIM_CheckCxxInsnPredicate, /*MI*/0, /*FnId*/GIPFP_MI_Predicate_frag, +// R21O-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID, +// R21O-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID, // R21N-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/4, // R21N-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_SELECT, +// R21N-NEXT: GIM_CheckCxxInsnPredicate, /*MI*/0, /*FnId*/GIPFP_MI_Predicate_frag, // R21N-NEXT: // MIs[0] dst // R21N-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32, // R21N-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID, @@ -370,7 +373,8 @@ def : Pat<(select GPR32:$src1, (complex_rr GPR32:$src2a, GPR32:$src2b), // R21N-NEXT: // MIs[0] src3 // R21N-NEXT: GIM_CheckType, /*MI*/0, /*Op*/3, /*Type*/GILLT_s32, // R21C-NEXT: GIM_CheckComplexPattern, /*MI*/0, /*Op*/3, /*Renderer*/1, GICP_gi_complex, -// R21C-NEXT: // (select:{ *:[i32] } GPR32:{ *:[i32] }:$src1, complex:{ *:[i32] }:$src2, complex:{ *:[i32] }:$src3) => (INSN2:{ *:[i32] } GPR32:{ *:[i32] }:$src1, complex:{ *:[i32] }:$src3, complex:{ *:[i32] }:$src2) +// R21C-NEXT: // (select:{ *:[i32] } GPR32:{ *:[i32] }:$src1, complex:{ *:[i32] }:$src2, complex:{ *:[i32] }:$src3)<> => (INSN2:{ *:[i32] } GPR32:{ *:[i32] }:$src1, complex:{ *:[i32] }:$src3, complex:{ *:[i32] }:$src2) + // R21C-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::INSN2, // R21C-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst // R21C-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src1 @@ -447,7 +451,12 @@ def : GINodeEquiv; let mayLoad = 1 in { def INSN2 : I<(outs GPR32:$dst), (ins GPR32Op:$src1, complex:$src2, complex:$src3), []>; } -def : Pat<(select GPR32:$src1, complex:$src2, complex:$src3), +def frag : PatFrag<(ops node:$a, node:$b, node:$c), + (select node:$a, node:$b, node:$c), + [{ return true; // C++ code }]> { + let GISelPredicateCode = [{ return true; // C++ code }]; +} +def : Pat<(frag GPR32:$src1, complex:$src2, complex:$src3), (INSN2 GPR32:$src1, complex:$src3, complex:$src2)>; //===- Test a more complex multi-instruction match. -----------------------===// diff --git a/llvm/utils/TableGen/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/CodeGenDAGPatterns.cpp index 610513b..699ff5d 100644 --- a/llvm/utils/TableGen/CodeGenDAGPatterns.cpp +++ b/llvm/utils/TableGen/CodeGenDAGPatterns.cpp @@ -1134,6 +1134,14 @@ Record *TreePredicateFn::getScalarMemoryVT() const { return nullptr; return R->getValueAsDef("ScalarMemoryVT"); } +bool TreePredicateFn::hasGISelPredicateCode() const { + return !PatFragRec->getRecord() + ->getValueAsString("GISelPredicateCode") + .empty(); +} +std::string TreePredicateFn::getGISelPredicateCode() const { + return PatFragRec->getRecord()->getValueAsString("GISelPredicateCode"); +} StringRef TreePredicateFn::getImmType() const { if (immCodeUsesAPInt()) diff --git a/llvm/utils/TableGen/CodeGenDAGPatterns.h b/llvm/utils/TableGen/CodeGenDAGPatterns.h index 405bb24..fc4297e 100644 --- a/llvm/utils/TableGen/CodeGenDAGPatterns.h +++ b/llvm/utils/TableGen/CodeGenDAGPatterns.h @@ -543,6 +543,10 @@ public: /// ValueType record for the memory VT. Record *getScalarMemoryVT() const; + // If true, indicates that GlobalISel-based C++ code was supplied. + bool hasGISelPredicateCode() const; + std::string getGISelPredicateCode() const; + private: bool hasPredCode() const; bool hasImmCode() const; diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp index ad461f5..61d17a5 100644 --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -84,6 +84,8 @@ namespace { /// Get the name of the enum value used to number the predicate function. std::string getEnumNameForPredicate(const TreePredicateFn &Predicate) { + if (Predicate.hasGISelPredicateCode()) + return "GIPFP_MI_" + Predicate.getFnName(); return "GIPFP_" + Predicate.getImmTypeIdentifier().str() + "_" + Predicate.getFnName(); } @@ -319,6 +321,9 @@ static Error isTrivialOperatorNode(const TreePatternNode *N) { Predicate.isAtomicOrderingWeakerThanRelease())) continue; + if (Predicate.hasGISelPredicateCode()) + continue; + HasUnsupportedPredicate = true; Explanation = Separator + "Has a predicate (" + explainPredicates(N) + ")"; Separator = ", "; @@ -1014,6 +1019,7 @@ public: IPM_AtomicOrderingMMO, IPM_MemoryLLTSize, IPM_MemoryVsLLTSize, + IPM_GenericPredicate, OPM_SameOperand, OPM_ComplexPattern, OPM_IntrinsicID, @@ -1817,6 +1823,30 @@ public: } }; +/// Generates code to check an arbitrary C++ instruction predicate. +class GenericInstructionPredicateMatcher : public InstructionPredicateMatcher { +protected: + TreePredicateFn Predicate; + +public: + GenericInstructionPredicateMatcher(unsigned InsnVarID, + TreePredicateFn Predicate) + : InstructionPredicateMatcher(IPM_GenericPredicate, InsnVarID), + Predicate(Predicate) {} + + static bool classof(const InstructionPredicateMatcher *P) { + return P->getKind() == IPM_GenericPredicate; + } + void emitPredicateOpcodes(MatchTable &Table, + RuleMatcher &Rule) const override { + Table << MatchTable::Opcode("GIM_CheckCxxInsnPredicate") + << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) + << MatchTable::Comment("FnId") + << MatchTable::NamedValue(getEnumNameForPredicate(Predicate)) + << MatchTable::LineBreak; + } +}; + /// Generates code to check that a set of predicates and operands match for a /// particular instruction. /// @@ -2954,20 +2984,25 @@ private: void gatherOpcodeValues(); void gatherTypeIDValues(); void gatherNodeEquivs(); + // Instruction predicate code that will be emitted in generated functions. + SmallVector InstructionPredicateCodes; + unsigned getOrCreateInstructionPredicateFnId(StringRef Code); + Record *findNodeEquiv(Record *N) const; const CodeGenInstruction *getEquivNode(Record &Equiv, const TreePatternNode *N) const; Error importRulePredicates(RuleMatcher &M, ArrayRef Predicates); - Expected createAndImportSelDAGMatcher( - RuleMatcher &Rule, InstructionMatcher &InsnMatcher, - const TreePatternNode *Src, unsigned &TempOpIdx) const; + Expected + createAndImportSelDAGMatcher(RuleMatcher &Rule, + InstructionMatcher &InsnMatcher, + const TreePatternNode *Src, unsigned &TempOpIdx); Error importComplexPatternOperandMatcher(OperandMatcher &OM, Record *R, unsigned &TempOpIdx) const; Error importChildMatcher(RuleMatcher &Rule, InstructionMatcher &InsnMatcher, const TreePatternNode *SrcChild, bool OperandIsAPointer, unsigned OpIdx, - unsigned &TempOpIdx) const; + unsigned &TempOpIdx); Expected createAndImportInstructionRenderer(RuleMatcher &M, @@ -2993,9 +3028,14 @@ private: importImplicitDefRenderers(BuildMIAction &DstMIBuilder, const std::vector &ImplicitDefs) const; - void emitImmPredicates(raw_ostream &OS, StringRef TypeIdentifier, - StringRef Type, - std::function Filter); + void emitCxxPredicateFns(raw_ostream &OS, StringRef CodeFieldName, + StringRef TypeIdentifier, StringRef ArgType, + StringRef ArgName, StringRef AdditionalDeclarations, + std::function Filter); + void emitImmPredicateFns(raw_ostream &OS, StringRef TypeIdentifier, + StringRef ArgType, + std::function Filter); + void emitMIPredicateFns(raw_ostream &OS); /// Analyze pattern \p P, returning a matcher for it if possible. /// Otherwise, return an Error explaining why we don't support it. @@ -3045,6 +3085,20 @@ void GlobalISelEmitter::gatherOpcodeValues() { void GlobalISelEmitter::gatherTypeIDValues() { LLTOperandMatcher::initTypeIDValuesMap(); } +unsigned GlobalISelEmitter::getOrCreateInstructionPredicateFnId(StringRef Code) { + // There's not very many predicates that need to be here at the moment so we + // just maintain a simple set-like vector. If it grows then we'll need to do + // something more efficient. + const auto &I = std::find(InstructionPredicateCodes.begin(), + InstructionPredicateCodes.end(), + Code); + if (I == InstructionPredicateCodes.end()) { + unsigned ID = InstructionPredicateCodes.size(); + InstructionPredicateCodes.push_back(Code); + return ID; + } + return std::distance(InstructionPredicateCodes.begin(), I); +} void GlobalISelEmitter::gatherNodeEquivs() { assert(NodeEquivs.empty()); @@ -3106,7 +3160,7 @@ GlobalISelEmitter::importRulePredicates(RuleMatcher &M, Expected GlobalISelEmitter::createAndImportSelDAGMatcher( RuleMatcher &Rule, InstructionMatcher &InsnMatcher, - const TreePatternNode *Src, unsigned &TempOpIdx) const { + const TreePatternNode *Src, unsigned &TempOpIdx) { Record *SrcGIEquivOrNull = nullptr; const CodeGenInstruction *SrcGIOrNull = nullptr; @@ -3251,6 +3305,11 @@ Expected GlobalISelEmitter::createAndImportSelDAGMatcher( } } + if (Predicate.hasGISelPredicateCode()) { + InsnMatcher.addPredicate(Predicate); + continue; + } + return failedImport("Src pattern child has predicate (" + explainPredicates(Src) + ")"); } @@ -3329,7 +3388,7 @@ Error GlobalISelEmitter::importChildMatcher(RuleMatcher &Rule, const TreePatternNode *SrcChild, bool OperandIsAPointer, unsigned OpIdx, - unsigned &TempOpIdx) const { + unsigned &TempOpIdx) { OperandMatcher &OM = InsnMatcher.addOperand(OpIdx, SrcChild->getName(), TempOpIdx); if (OM.isSameAsAnotherOperand()) @@ -3991,14 +4050,15 @@ Expected GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { // Emit imm predicate table and an enum to reference them with. // The 'Predicate_' part of the name is redundant but eliminating it is more // trouble than it's worth. -void GlobalISelEmitter::emitImmPredicates( - raw_ostream &OS, StringRef TypeIdentifier, StringRef Type, +void GlobalISelEmitter::emitCxxPredicateFns( + raw_ostream &OS, StringRef CodeFieldName, StringRef TypeIdentifier, + StringRef ArgType, StringRef ArgName, StringRef AdditionalDeclarations, std::function Filter) { std::vector MatchedRecords; const auto &Defs = RK.getAllDerivedDefinitions("PatFrag"); std::copy_if(Defs.begin(), Defs.end(), std::back_inserter(MatchedRecords), [&](Record *Record) { - return !Record->getValueAsString("ImmediateCode").empty() && + return !Record->getValueAsString(CodeFieldName).empty() && Filter(Record); }); @@ -4015,16 +4075,20 @@ void GlobalISelEmitter::emitImmPredicates( OS << "};\n"; } - OS << "bool " << Target.getName() << "InstructionSelector::testImmPredicate_" - << TypeIdentifier << "(unsigned PredicateID, " << Type - << " Imm) const {\n"; + OS << "bool " << Target.getName() << "InstructionSelector::test" << ArgName + << "Predicate_" << TypeIdentifier << "(unsigned PredicateID, " << ArgType << " " + << ArgName << ") const {\n" + << AdditionalDeclarations; + if (!AdditionalDeclarations.empty()) + OS << "\n"; if (!MatchedRecords.empty()) OS << " switch (PredicateID) {\n"; for (const auto *Record : MatchedRecords) { OS << " case GIPFP_" << TypeIdentifier << "_Predicate_" << Record->getName() << ": {\n" - << " " << Record->getValueAsString("ImmediateCode") << "\n" - << " llvm_unreachable(\"ImmediateCode should have returned\");\n" + << " " << Record->getValueAsString(CodeFieldName) << "\n" + << " llvm_unreachable(\"" << CodeFieldName + << " should have returned\");\n" << " return false;\n" << " }\n"; } @@ -4035,6 +4099,22 @@ void GlobalISelEmitter::emitImmPredicates( << "}\n"; } +void GlobalISelEmitter::emitImmPredicateFns( + raw_ostream &OS, StringRef TypeIdentifier, StringRef ArgType, + std::function Filter) { + return emitCxxPredicateFns(OS, "ImmediateCode", TypeIdentifier, ArgType, + "Imm", "", Filter); +} + +void GlobalISelEmitter::emitMIPredicateFns(raw_ostream &OS) { + return emitCxxPredicateFns( + OS, "GISelPredicateCode", "MI", "const MachineInstr &", "MI", + " const MachineFunction &MF = *MI.getParent()->getParent();\n" + " const LLVM_ATTRIBUTE_UNUSED MachineRegisterInfo &MRI = " + "MF.getRegInfo();", + [](const Record *R) { return true; }); +} + template std::vector GlobalISelEmitter::optimizeRules( ArrayRef Rules, @@ -4261,6 +4341,8 @@ void GlobalISelEmitter::run(raw_ostream &OS) { << " bool testImmPredicate_APFloat(unsigned PredicateID, const APFloat " "&Imm) const override;\n" << " const int64_t *getMatchTable() const override;\n" + << " bool testMIPredicate_MI(unsigned PredicateID, const MachineInstr &MI) " + "const override;\n" << "#endif // ifdef GET_GLOBALISEL_TEMPORARIES_DECL\n\n"; OS << "#ifdef GET_GLOBALISEL_TEMPORARIES_INIT\n" @@ -4374,18 +4456,19 @@ void GlobalISelEmitter::run(raw_ostream &OS) { OS << "};\n" << "// See constructor for table contents\n\n"; - emitImmPredicates(OS, "I64", "int64_t", [](const Record *R) { + emitImmPredicateFns(OS, "I64", "int64_t", [](const Record *R) { bool Unset; return !R->getValueAsBitOrUnset("IsAPFloat", Unset) && !R->getValueAsBit("IsAPInt"); }); - emitImmPredicates(OS, "APFloat", "const APFloat &", [](const Record *R) { + emitImmPredicateFns(OS, "APFloat", "const APFloat &", [](const Record *R) { bool Unset; return R->getValueAsBitOrUnset("IsAPFloat", Unset); }); - emitImmPredicates(OS, "APInt", "const APInt &", [](const Record *R) { + emitImmPredicateFns(OS, "APInt", "const APInt &", [](const Record *R) { return R->getValueAsBit("IsAPInt"); }); + emitMIPredicateFns(OS); OS << "\n"; OS << Target.getName() << "InstructionSelector::ComplexMatcherMemFn\n"