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 @@ -39,6 +39,9 @@ class GICombineRule : GICombine { /// Defines the external interface of the match rule. This includes: /// * The names of the root nodes (requires at least one) + /// * The names of any immediate operands. + /// * The names of any complex operands. + /// * Any macro substitutions/alternatives that should apply to this rule. /// See GIDefKind for details. dag Defs = defs; @@ -52,6 +55,12 @@ dag Apply = apply; } +class GIType { + string CxxType = cxxType; +} +def machineinstr : GIType<"MachineInstr&">; +def machineoperand : GIType<"MachineOperand&">; + /// The operator at the root of a GICombineRule.Defs dag. def defs; @@ -93,6 +102,22 @@ /// performed with this. def wip_match_opcode : GIMatchKindWithArgs; +class GIMatchPredicate : GIMatchKindWithArgs { + string ReturnType = returnType; + dag Arguments = arguments; + dag Results = results; + code Predicate = predicate; +} +def is_same_valid_type : GIMatchPredicate< + "bool", (ins machineoperand:$A, machineoperand:$B), (outs), [{ + if (!${A}.isReg() || !${B}.isReg()) + return false; + LLT ATy = MRI->getType(${A}.getReg()); + LLT BTy = MRI->getType(${B}.getReg()); + return ATy.isValid() && BTy.isValid() && ATy == BTy; +}]>; + /// The operator at the root of a GICombineRule.Apply dag. def apply; /// All arguments of the apply operator must be subclasses of GIApplyKind, or @@ -102,10 +127,11 @@ class GIApplyKindWithArgs; def copy_prop : GICombineRule< - (defs root:$d), - (match (COPY $d, $s):$mi, - [{ return Helper.matchCombineCopy(*${mi}); }]), - (apply [{ Helper.applyCombineCopy(*${mi}); }])>; + (defs root:$root), + (match (COPY $d, $s):$root, + (is_same_valid_type $d, $s), + [{ return Helper.matchCombineCopy(*${root}); }]), + (apply [{ Helper.applyCombineCopy(*${root}); }])>; def trivial_combines : GICombineGroup<[copy_prop]>; def extending_loads : GICombineRule< diff --git a/llvm/utils/TableGen/GICombinerEmitter.cpp b/llvm/utils/TableGen/GICombinerEmitter.cpp --- a/llvm/utils/TableGen/GICombinerEmitter.cpp +++ b/llvm/utils/TableGen/GICombinerEmitter.cpp @@ -40,7 +40,7 @@ static cl::list SelectedCombiners("combiners", cl::desc("Emit the specified combiners"), cl::cat(GICombinerEmitterCat), cl::CommaSeparated); -static cl::opt ShowExpansions( +cl::opt ShowExpansions( "gicombiner-show-expansions", cl::desc("Use C++ comments to indicate occurence of code expansion"), cl::cat(GICombinerEmitterCat)); @@ -126,6 +126,15 @@ : N(N), Op(Op), Matcher(Matcher) {} }; + struct PredInfo { + const GIMatchDagPredicate *P; + const GIMatchDagOperand *Op; + + public: + PredInfo(const GIMatchDagPredicate *P, const GIMatchDagOperand *Op) + : P(P), Op(Op) {} + }; + protected: /// A unique ID for this rule /// ID's are used for debugging and run-time disabling of rules among other @@ -185,6 +194,9 @@ StringMap> &NamedEdgeUses); bool parseWipMatchOpcodeMatcher(const CodeGenTarget &Target, StringInit *ArgName, const Init &Arg); + bool parseMatchPredicateMatcher( + const CodeGenTarget &Target, StringInit *ArgName, const Init &Arg, + StringMap> &PredicateDepsByName); public: CombineRule(const CodeGenTarget &Target, GIMatchDagContext &Ctx, RuleID ID, @@ -410,6 +422,11 @@ GIMatchDagInstr *N = MatchDag.addInstrNode(makeDebugName(*this, Name), insertStrTab(Name), MatchDag.getContext().makeOperandList(Instr)); + if (find_if(Roots, [&](const RootInfo &X) { + return ArgName && X.getPatternSymbol() == ArgName->getValue(); + }) != Roots.end()) { + N->setMatchRoot(); + } N->setOpcodeAnnotation(&Instr); const auto &P = MatchDag.addPredicateNode( @@ -487,11 +504,54 @@ } return false; } + +bool CombineRule::parseMatchPredicateMatcher( + const CodeGenTarget &Target, StringInit *ArgName, const Init &Arg, + StringMap> &PredicateDepsByName) { + if (const DagInit *Matcher = + getDagWithOperatorOfSubClass(Arg, "GIMatchPredicate")) { + const Record *PredDef = Matcher->getOperatorAsDef(TheDef.getLoc()); + + auto *P = MatchDag.addPredicateNode( + makeNameForAnonPredicate(*this), *PredDef); + + DagInit *PredicateArgs = PredDef->getValueAsDag("Arguments"); + DagInit *PredicateResults = PredDef->getValueAsDag("Results"); + + if (PredicateArgs->getNumArgs() + PredicateResults->getNumArgs() != Matcher->getNumArgs()) { + PrintError(TheDef.getLoc(), "Unexpected number of operands, received " + + llvm::to_string(Matcher->getNumArgs())); + PrintNote(PredDef->getLoc(), + "but expected " + + llvm::to_string(PredicateArgs->getNumArgs() + + PredicateResults->getNumArgs())); + return false; + } + + if (PredicateResults->getNumArgs() > 0) { + llvm_unreachable("Not implemented"); + return false; + } + + // Each argument is an argument to the predicate. Add a dependency for each + // one that references something in the DAG. + for (unsigned I = 0; I < Matcher->getNumArgs(); ++I) { + StringRef VarName = Matcher->getArgName(I)->getValue(); + PredicateDepsByName.try_emplace(VarName); + PredicateDepsByName[VarName].emplace_back(P, &P->getOperandInfo()[I]); + P->bind(VarName); + } + return true; + } + return false; +} + bool CombineRule::parseMatcher(const CodeGenTarget &Target) { NamedRegionTimer T("parseMatcher", "Time spent parsing the matcher", "Rule Parsing", "Time spent on rule parsing", TimeRegions); StringMap> NamedEdgeDefs; StringMap> NamedEdgeUses; + StringMap> PredicateDepsByName; DagInit *Matchers = TheDef.getValueAsDag("Match"); if (Matchers->getOperatorAsDef(TheDef.getLoc())->getName() != "match") { @@ -516,6 +576,10 @@ *Matchers->getArg(I))) continue; + if (parseMatchPredicateMatcher(Target, Matchers->getArgName(I), + *Matchers->getArg(I), + PredicateDepsByName)) + continue; // Parse arbitrary C++ code we have in lieu of supporting MIR matching if (const CodeInit *CodeI = dyn_cast(Matchers->getArg(I))) { @@ -555,6 +619,26 @@ if (FailedToAddEdges) return false; + // Add the dependencies from variables to predicates. + for (const auto &PredNameAndDeps : PredicateDepsByName) { + StringRef VarName = PredNameAndDeps.getKey(); + FailedToAddEdges = true; + for (const PredInfo &P : PredNameAndDeps.getValue()) { + for (const VarInfo &UseVar : NamedEdgeUses[VarName]) { + MatchDag.addPredicateDependency(UseVar.N, UseVar.Op, P.P, P.Op); + FailedToAddEdges = false; + } + for (const VarInfo &DefVar : NamedEdgeDefs[VarName]) { + MatchDag.addPredicateDependency(DefVar.N, DefVar.Op, P.P, P.Op); + FailedToAddEdges = false; + } + } + if (FailedToAddEdges) { + PrintError(TheDef.getLoc(), "Undefined variable " + VarName); + return false; + } + } + // If a variable is referenced in multiple use contexts then we need a // predicate to confirm they are the same operand. We can elide this if it's // also referenced in a def context and we're traversing the def-use chain @@ -618,7 +702,9 @@ /// response to the generated cl::opt. void emitNameMatcher(raw_ostream &OS) const; - void generateDeclarationsCodeForTree(raw_ostream &OS, const GIMatchTree &Tree) const; + void generateDeclarationsCodeForTree(raw_ostream &HeaderOS, + raw_ostream &BodyOS, + const GIMatchTree &Tree) const; void generateCodeForTree(raw_ostream &OS, const GIMatchTree &Tree, StringRef Indent) const; }; @@ -720,6 +806,19 @@ } } +void GICombinerEmitter::generateDeclarationsCodeForTree( + raw_ostream &HeaderOS, raw_ostream &BodyOS, const GIMatchTree &Tree) const { + if (Tree.getPartitioner() != nullptr) { + for (const auto &Child : Tree.children()) + generateDeclarationsCodeForTree(HeaderOS, BodyOS, Child); + return; + } + + for (const auto &Leaf : Tree.possible_leaves()) + for (const GIMatchDagPredicate *Predicate : Leaf.untested_predicates()) + Predicate->generateDeclarationsCode(HeaderOS, BodyOS, getClassName()); +} + void GICombinerEmitter::generateCodeForTree(raw_ostream &OS, const GIMatchTree &Tree, StringRef Indent) const { @@ -888,10 +987,16 @@ << "} // end namespace llvm\n" << "#endif // ifdef " << Name.upper() << "_GENCOMBINERHELPER_DEPS\n\n"; + std::string PredicatesHeader, PredicatesBody; + raw_string_ostream PredicatesHeaderOS(PredicatesHeader); + raw_string_ostream PredicatesBodyOS(PredicatesBody); + generateDeclarationsCodeForTree(PredicatesHeaderOS, PredicatesBodyOS, *Tree); + OS << "#ifdef " << Name.upper() << "_GENCOMBINERHELPER_H\n" << "class " << getClassName() << " {\n" << " SparseBitVector<> DisabledRules;\n" << "\n" + << PredicatesHeaderOS.str() << "public:\n" << " bool parseCommandLineOption();\n" << " bool isRuleDisabled(unsigned ID) const;\n" @@ -902,6 +1007,18 @@ << " MachineInstr &MI,\n" << " MachineIRBuilder &B,\n" << " CombinerHelper &Helper) const;\n" + << "\n" + << "private:\n" + << " /// These mutable fields are accessors set by the entry point for\n" + << " /// use by the C++ predicates. They must be set by every try*()\n" + << " /// entry point.\n" + << "\n" + << " /// Parent MBB for the current MI being processed\n" + << " mutable MachineBasicBlock *MBB = nullptr;\n" + << " /// Parent MF for the current MI being processed\n" + << " mutable MachineFunction *MF = nullptr;\n" + << " /// MRI for the current MI being processed\n" + << " mutable MachineRegisterInfo *MRI = nullptr;\n" << "};\n\n"; emitNameMatcher(OS); @@ -952,16 +1069,18 @@ << " if (!setRuleDisabled(Identifier))\n" << " return false;\n" << " return true;\n" - << "}\n\n"; + << "}\n\n" + << PredicatesBodyOS.str() << "\n\n"; + OS << "\n"; OS << "bool " << getClassName() << "::tryCombineAll(\n" << " GISelChangeObserver &Observer,\n" << " MachineInstr &MI,\n" << " MachineIRBuilder &B,\n" << " CombinerHelper &Helper) const {\n" - << " MachineBasicBlock *MBB = MI.getParent();\n" - << " MachineFunction *MF = MBB->getParent();\n" - << " MachineRegisterInfo &MRI = MF->getRegInfo();\n" + << " MBB = MI.getParent();\n" + << " MF = MBB->getParent();\n" + << " MRI = &MF->getRegInfo();\n" << " SmallVector MIs = { &MI };\n\n" << " (void)MBB; (void)MF; (void)MRI;\n\n"; diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDag.h b/llvm/utils/TableGen/GlobalISel/GIMatchDag.h --- a/llvm/utils/TableGen/GlobalISel/GIMatchDag.h +++ b/llvm/utils/TableGen/GlobalISel/GIMatchDag.h @@ -15,6 +15,8 @@ #include "GIMatchDagPredicate.h" #include "GIMatchDagPredicateDependencyEdge.h" +#include "llvm/TableGen/Record.h" + namespace llvm { class GIMatchDag; @@ -35,6 +37,10 @@ return OperandListCtx.makeMIPredicateOperandList(); } + const GIMatchDagOperandList & + makeGIMatchDagPredicateOperandList(const Record &TheDef) { + return OperandListCtx.makeGIMatchDagPredicateOperandList(TheDef); + } const GIMatchDagOperandList &makeTwoMOPredicateOperandList() { return OperandListCtx.makeTwoMOPredicateOperandList(); diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDagInstr.h b/llvm/utils/TableGen/GlobalISel/GIMatchDagInstr.h --- a/llvm/utils/TableGen/GlobalISel/GIMatchDagInstr.h +++ b/llvm/utils/TableGen/GlobalISel/GIMatchDagInstr.h @@ -72,9 +72,14 @@ : Dag(Dag), Name(Name), UserAssignedName(UserAssignedName), OperandInfo(OperandInfo) {} + const GIMatchDag &getDag() const { return Dag; } const GIMatchDagOperandList &getOperandInfo() const { return OperandInfo; } StringRef getName() const { return Name; } StringRef getUserAssignedName() const { return UserAssignedName; } + StringRef getUserAssignedNameForOperand(unsigned Idx) const { + return UserAssignedNamesForOperands.lookup(Idx); + } + void assignNameToOperand(unsigned Idx, StringRef Name) { assert(UserAssignedNamesForOperands[Idx].empty() && "Cannot assign twice"); UserAssignedNamesForOperands[Idx] = Name; diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDagOperands.h b/llvm/utils/TableGen/GlobalISel/GIMatchDagOperands.h --- a/llvm/utils/TableGen/GlobalISel/GIMatchDagOperands.h +++ b/llvm/utils/TableGen/GlobalISel/GIMatchDagOperands.h @@ -15,12 +15,16 @@ #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" #include namespace llvm { class CodeGenInstruction; +class Record; + /// Describes an operand of a MachineInstr w.r.t the DAG Matching. This /// information is derived from CodeGenInstruction::Operands but is more /// readily available for context-less access as we don't need to know which @@ -102,6 +106,8 @@ iterator end() { return Operands.end(); } const_iterator end() const { return Operands.end(); } + size_t size() const { return Operands.size(); } + const value_type &operator[](unsigned I) const { return Operands[I]; } const value_type &operator[](StringRef K) const; @@ -121,6 +127,8 @@ const GIMatchDagOperandList &makeEmptyOperandList(); const GIMatchDagOperandList &makeOperandList(const CodeGenInstruction &I); const GIMatchDagOperandList &makeMIPredicateOperandList(); + const GIMatchDagOperandList & + makeGIMatchDagPredicateOperandList(const Record &TheDef); const GIMatchDagOperandList &makeTwoMOPredicateOperandList(); void print(raw_ostream &OS) const; diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDagOperands.cpp b/llvm/utils/TableGen/GlobalISel/GIMatchDagOperands.cpp --- a/llvm/utils/TableGen/GlobalISel/GIMatchDagOperands.cpp +++ b/llvm/utils/TableGen/GlobalISel/GIMatchDagOperands.cpp @@ -9,6 +9,7 @@ #include "GIMatchDagOperands.h" #include "../CodeGenInstruction.h" +#include "llvm/TableGen/Record.h" using namespace llvm; @@ -116,6 +117,38 @@ return *OperandListsOwner.back().get(); } +const GIMatchDagOperandList & +GIMatchDagOperandListContext::makeGIMatchDagPredicateOperandList( + const Record &TheDef) { + FoldingSetNodeID ID; + DagInit *PredicateArgs = TheDef.getValueAsDag("Arguments"); + unsigned OpIdx = 0; + for (unsigned I = 0; I < PredicateArgs->getNumArgs(); ++I) + GIMatchDagOperand::Profile(ID, OpIdx++, + PredicateArgs->getArgName(I)->getValue(), false); + DagInit *PredicateResults = TheDef.getValueAsDag("Results"); + for (unsigned I = 0; I < PredicateResults->getNumArgs(); ++I) + GIMatchDagOperand::Profile(ID, OpIdx++, + PredicateArgs->getArgName(I)->getValue(), true); + + void *InsertPoint; + GIMatchDagOperandList *Value = + OperandLists.FindNodeOrInsertPos(ID, InsertPoint); + if (Value) + return *Value; + + std::unique_ptr NewValue = + std::make_unique(); + OpIdx = 0; + for (unsigned I = 0; I < PredicateArgs->getNumArgs(); ++I) + NewValue->add(PredicateArgs->getArgName(I)->getValue(), OpIdx++, false); + for (unsigned I = 0; I < PredicateResults->getNumArgs(); ++I) + NewValue->add(PredicateArgs->getArgName(I)->getValue(), OpIdx++, true); + + OperandLists.InsertNode(NewValue.get(), InsertPoint); + OperandListsOwner.push_back(std::move(NewValue)); + return *OperandListsOwner.back().get(); +} const GIMatchDagOperandList & GIMatchDagOperandListContext::makeTwoMOPredicateOperandList() { diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicate.h b/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicate.h --- a/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicate.h +++ b/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicate.h @@ -9,6 +9,7 @@ #ifndef LLVM_UTILS_TABLEGEN_GIMATCHDAGPREDICATE_H #define LLVM_UTILS_TABLEGEN_GIMATCHDAGPREDICATE_H +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringRef.h" #include "GIMatchDag.h" @@ -17,6 +18,7 @@ class CodeGenInstruction; class GIMatchDagOperandList; class GIMatchDagContext; +class Record; class raw_ostream; /// Represents a predicate on the match DAG. This records the details of the @@ -30,6 +32,7 @@ GIMatchDagPredicateKind_Opcode, GIMatchDagPredicateKind_OneOfOpcodes, GIMatchDagPredicateKind_SameMO, + GIMatchDagPredicateKind_ArbitraryCxxPredicate, }; protected: @@ -58,6 +61,11 @@ StringRef getName() const { return Name; } const GIMatchDagOperandList &getOperandInfo() const { return OperandInfo; } + // Generate C++ code for declarations this predicate will need. + virtual void generateDeclarationsCode(raw_ostream &HeaderOS, + raw_ostream &BodyOS, + StringRef ClassName) const {} + // Generate C++ code to check this predicate. If a partitioner has already // tested this predicate then this function won't be called. If this function // is called, it must emit code and return true to indicate that it did so. If @@ -134,8 +142,38 @@ #endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) }; +class GIMatchDagArbitraryCxxPredicate : public GIMatchDagPredicate { + const Record &TheDef; + SmallVector Args; + + static SmallPtrSet Emitted; + +public: + GIMatchDagArbitraryCxxPredicate(GIMatchDagContext &Ctx, StringRef Name, + const Record &TheDef); + + static bool classof(const GIMatchDagPredicate *P) { + return P->getKind() == GIMatchDagPredicateKind_ArbitraryCxxPredicate; + } + + void bind(StringRef Name) { Args.push_back(Name); } + + void generateDeclarationsCode(raw_ostream &HeaderOS, raw_ostream &BodyOS, + StringRef ClassName) const override; + bool generateCheckCode(raw_ostream &OS, StringRef Indent, + const CodeExpansions &Expansions) const override; + + void printDescription(raw_ostream &OS) const override; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + virtual LLVM_DUMP_METHOD void dump() const override { print(errs()); } +#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +}; + raw_ostream &operator<<(raw_ostream &OS, const GIMatchDagPredicate &N); raw_ostream &operator<<(raw_ostream &OS, const GIMatchDagOpcodePredicate &N); +raw_ostream &operator<<(raw_ostream &OS, const GIMatchDagSameMOPredicate &N); +raw_ostream &operator<<(raw_ostream &OS, const GIMatchDagArbitraryCxxPredicate &N); } // end namespace llvm #endif // ifndef LLVM_UTILS_TABLEGEN_GIMATCHDAGPREDICATE_H diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicate.cpp b/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicate.cpp --- a/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicate.cpp +++ b/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicate.cpp @@ -8,13 +8,19 @@ #include "GIMatchDagPredicate.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" +#include "CodeExpansions.h" +#include "CodeExpander.h" #include "GIMatchDagOperands.h" #include "../CodeGenInstruction.h" using namespace llvm; +extern cl::opt ShowExpansions; + void GIMatchDagPredicate::print(raw_ostream &OS) const { OS << "<<"; printDescription(OS); @@ -57,6 +63,67 @@ OS << "$mi0 == $mi1"; } +SmallPtrSet GIMatchDagArbitraryCxxPredicate::Emitted = {}; + +GIMatchDagArbitraryCxxPredicate::GIMatchDagArbitraryCxxPredicate( + GIMatchDagContext &Ctx, StringRef Name, const Record &TheDef) + : GIMatchDagPredicate(GIMatchDagPredicateKind_ArbitraryCxxPredicate, Name, + Ctx.makeGIMatchDagPredicateOperandList(TheDef)), + TheDef(TheDef) {} + +void GIMatchDagArbitraryCxxPredicate::printDescription(raw_ostream &OS) const { + OS << "call predicate " << TheDef.getName() << "("; + OperandInfo.print(OS); + OS << ")"; +} + +void GIMatchDagArbitraryCxxPredicate::generateDeclarationsCode( + raw_ostream &HeaderOS, raw_ostream &BodyOS, StringRef ClassName) const { + if (!Emitted.count(&TheDef)) { + Emitted.insert(&TheDef); + + HeaderOS << " " << TheDef.getValueAsString("ReturnType") << " " + << "testPredicate_" << TheDef.getName() << "("; + BodyOS << TheDef.getValueAsString("ReturnType") << " " << ClassName + << "::testPredicate_" << TheDef.getName() << "("; + + DagInit *Arguments = TheDef.getValueAsDag("Arguments"); + CodeExpansions Expansions; + StringRef Separator = ""; + for (unsigned i = 0; i < Arguments->getNumArgs(); ++i) { + DefInit *Arg = dyn_cast(Arguments->getArg(i)); + if (!Arg || !Arg->getDef()->isSubClassOf("GIType")) { + PrintError(TheDef.getLoc(), "Expected subclass of GIType"); + continue; + } + StringRef Type = Arg->getDef()->getValueAsString("CxxType"); + StringRef Name = Arguments->getArgName(i)->getValue(); + HeaderOS << Separator << Type << " " << Name; + BodyOS << Separator << Type << " " << Name; + Separator = ", "; + + Expansions.declare(Name, Name); + } + HeaderOS << ") const; \n"; + BodyOS << ") const {\n " + << CodeExpander(TheDef.getValueAsString("Predicate").trim(" \t\n"), + Expansions, TheDef.getLoc(), ShowExpansions, "") + << "\n}\n"; + } +} + +bool GIMatchDagArbitraryCxxPredicate::generateCheckCode( + raw_ostream &OS, StringRef Indent, const CodeExpansions &Expansions) const { + OS << Indent << "&& testPredicate_" << TheDef.getName() << "("; + StringRef Separator = ""; + for (StringRef Name : Args) { + OS << Separator << Expansions.lookup(Name); + Separator = ", "; + } + OS << ")\n"; + return true; +} + raw_ostream &llvm::operator<<(raw_ostream &OS, const GIMatchDagPredicate &N) { N.print(OS); return OS; @@ -67,3 +134,15 @@ N.print(OS); return OS; } + +raw_ostream &llvm::operator<<(raw_ostream &OS, + const GIMatchDagSameMOPredicate &N) { + N.print(OS); + return OS; +} + +raw_ostream &llvm::operator<<(raw_ostream &OS, + const GIMatchDagArbitraryCxxPredicate &N) { + N.print(OS); + return OS; +}