//===- GlobalISelCombinerMatchTableEmitter.cpp - --------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // /// \file Generate a combiner implementation for GlobalISel from a declarative /// syntax using GlobalISelMatchTable. /// //===----------------------------------------------------------------------===// #include "CodeGenInstruction.h" #include "CodeGenTarget.h" #include "GlobalISel/CodeExpander.h" #include "GlobalISel/CodeExpansions.h" #include "GlobalISel/CombinerUtils.h" #include "GlobalISelMatchTable.h" #include "GlobalISelMatchTableExecutorEmitter.h" #include "SubtargetFeatureInfo.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/StringMatcher.h" #include "llvm/TableGen/TableGenBackend.h" #include using namespace llvm; using namespace llvm::gi; #define DEBUG_TYPE "gicombiner-matchtable-emitter" extern cl::list SelectedCombiners; extern cl::opt StopAfterParse; namespace { constexpr StringLiteral CXXApplyPrefix = "GICXXCustomAction_CombineApply"; constexpr StringLiteral CXXPredPrefix = "GICXXPred_MI_Predicate_"; std::string getIsEnabledPredicateEnumName(unsigned CombinerRuleID) { return "GICXXPred_Simple_IsRule" + to_string(CombinerRuleID) + "Enabled"; } void declareInstExpansion(CodeExpansions &CE, const InstructionMatcher &IM, StringRef Name) { CE.declare(Name, "State.MIs[" + to_string(IM.getInsnVarID()) + "]"); } void declareOperandExpansion(CodeExpansions &CE, const OperandMatcher &OM, StringRef Name) { CE.declare(Name, "State.MIs[" + to_string(OM.getInsnVarID()) + "]->getOperand(" + to_string(OM.getOpIdx()) + ")"); } template auto keys(Container &&C) { return map_range(C, [](auto &Entry) -> auto & { return Entry.first; }); } template auto values(Container &&C) { return map_range(C, [](auto &Entry) -> auto & { return Entry.second; }); } //===- MatchData Handling -------------------------------------------------===// /// Represents MatchData defined by the match stage and required by the apply /// stage. /// /// This allows the plumbing of arbitrary data from C++ predicates between the /// stages. /// /// When this class is initially created, it only has a pattern symbol and a /// type. When all of the MatchDatas declarations of a given pattern have been /// parsed, `AssignVariables` must be called to assign storage variable names to /// each MatchDataInfo. class MatchDataInfo { StringRef PatternSymbol; StringRef Type; std::string VarName; public: static constexpr StringLiteral StructTypeName = "MatchInfosTy"; static constexpr StringLiteral StructName = "MatchInfos"; MatchDataInfo(StringRef PatternSymbol, StringRef Type) : PatternSymbol(PatternSymbol), Type(Type.trim()) {} StringRef getPatternSymbol() const { return PatternSymbol; }; StringRef getType() const { return Type; }; bool hasVariableName() const { return !VarName.empty(); } void setVariableName(StringRef Name) { VarName = Name; } StringRef getVariableName() const; std::string getQualifiedVariableName() const { return StructName.str() + "." + getVariableName().str(); } void print(raw_ostream &OS) const; void dump() const { print(dbgs()); } }; StringRef MatchDataInfo::getVariableName() const { assert(hasVariableName()); return VarName; } void MatchDataInfo::print(raw_ostream &OS) const { OS << "(MatchDataInfo pattern_symbol:" << PatternSymbol << " type:'" << Type << "' var_name:" << (VarName.empty() ? "" : VarName) << ")"; } /// Pool of type -> variables used to emit MatchData variables declarations. /// /// e.g. if the map contains "int64_t" -> ["MD0", "MD1"], then two variable /// declarations must be emitted: `int64_t MD0` and `int64_t MD1`. /// /// This has a static lifetime and will outlive all the `MatchDataInfo` objects /// by design. It needs to persist after all `CombineRuleBuilder` objects died /// so we can emit the variable declarations. StringMap> AllMatchDataVars; // Assign variable names to all MatchDatas used by a pattern. This must be // called after all MatchData decls have been parsed inside a rule. // // Requires an array of MatchDataInfo so we can handle cases where a pattern // uses multiple instances of the same MatchData type. void AssignMatchDataVariables(MutableArrayRef Infos) { static unsigned NextVarID = 0; StringMap SeenTypes; for (auto &I : Infos) { unsigned &NumSeen = SeenTypes[I.getType()]; auto &ExistingVars = AllMatchDataVars[I.getType()]; if (NumSeen == ExistingVars.size()) ExistingVars.push_back("MDInfo" + to_string(NextVarID++)); I.setVariableName(ExistingVars[NumSeen++]); } } //===- C++ Predicates Handling --------------------------------------------===// /// Entry into the static pool of all CXX Predicate code. This contains the /// fully expanded C++ code. /// /// Each CXXPattern creates a new entry in the pool to store its data, even /// after the pattern is destroyed. /// /// Note that CXXPattern trims C++ code, so the Code is already expected to be /// free of leading/trailing whitespace. struct CXXPredicateCode { CXXPredicateCode(std::string Code, unsigned ID) : Code(Code), ID(ID), BaseEnumName("GICombiner" + to_string(ID)) { assert(StringRef(Code).trim() == Code && "Code was expected to be trimmed!"); } const std::string Code; const unsigned ID; const std::string BaseEnumName; bool needsUnreachable() const { return !StringRef(Code).starts_with("return"); } std::string getEnumNameWithPrefix(StringRef Prefix) const { return Prefix.str() + BaseEnumName; } }; using CXXPredicateCodePool = DenseMap>; CXXPredicateCodePool AllCXXMatchCode; CXXPredicateCodePool AllCXXApplyCode; /// Gets an instance of `CXXPredicateCode` for \p Code, or returns an already /// existing one. const CXXPredicateCode &getOrInsert(CXXPredicateCodePool &Pool, std::string Code) { // Check if we already have an identical piece of code, if not, create an // entry in the pool. const auto CodeHash = hash_value(Code); if (auto It = Pool.find(CodeHash); It != Pool.end()) return *It->second; const auto ID = Pool.size(); auto OwnedData = std::make_unique(std::move(Code), ID); const auto &DataRef = *OwnedData; Pool[CodeHash] = std::move(OwnedData); return DataRef; } /// Sorts a `CXXPredicateCodePool` by their IDs and returns it. std::vector getSorted(const CXXPredicateCodePool &Pool) { std::vector Out; std::transform(Pool.begin(), Pool.end(), std::back_inserter(Out), [&](auto &Elt) { return Elt.second.get(); }); sort(Out, [](const auto *A, const auto *B) { return A->ID < B->ID; }); return Out; } //===- Pattern Base Class -------------------------------------------------===// // An abstract pattern found in a combine rule. This can be an apply or match // pattern. class Pattern { public: enum { K_AnyOpcode, K_Inst, K_CXX, }; virtual ~Pattern() = default; unsigned getKind() const { return Kind; } const char *getKindName() const; bool hasName() const { return !Name.empty(); } StringRef getName() const { return Name; } virtual void print(raw_ostream &OS, bool PrintName = true) const = 0; void dump() const { return print(dbgs()); } protected: Pattern(unsigned Kind, StringRef Name) : Kind(Kind), Name(Name.str()) { assert(!Name.empty() && "unnamed pattern!"); } void printImpl(raw_ostream &OS, bool PrintName, function_ref ContentPrinter) const; private: unsigned Kind; // Note: if this ever changes to a StringRef (e.g. allocated in a pool or // something), CombineRuleBuilder::verify() needs to be updated as well. // It currently checks that the StringRef in the PatternMap references this. std::string Name; }; const char *Pattern::getKindName() const { switch (Kind) { case K_AnyOpcode: return "AnyOpcodePattern"; case K_Inst: return "InstructionPattern"; case K_CXX: return "CXXPattern"; } llvm_unreachable("unknown pattern kind!"); } void Pattern::printImpl(raw_ostream &OS, bool PrintName, function_ref ContentPrinter) const { OS << "(" << getKindName() << " "; if (PrintName) OS << "name:" << getName() << " "; ContentPrinter(); OS << ")"; } //===- AnyOpcodePattern ---------------------------------------------------===// /// `wip_match_opcode` patterns. /// This matches one or more opcodes, and does not check any operands /// whatsoever. class AnyOpcodePattern : public Pattern { public: AnyOpcodePattern(StringRef Name) : Pattern(K_AnyOpcode, Name) {} static bool classof(const Pattern *P) { return P->getKind() == K_AnyOpcode; } void addOpcode(const CodeGenInstruction *I) { Insts.push_back(I); } const auto &insts() const { return Insts; } void print(raw_ostream &OS, bool PrintName = true) const override; private: SmallVector Insts; }; void AnyOpcodePattern::print(raw_ostream &OS, bool PrintName) const { printImpl(OS, PrintName, [&OS, this]() { OS << "[" << join(map_range(Insts, [](const auto *I) { return I->TheDef->getName(); }), ", ") << "]"; }); } //===- InstructionPattern -------------------------------------------------===// /// Matches an instruction, e.g. `G_ADD $x, $y, $z`. /// /// This pattern is simply CodeGenInstruction + a list of operands. class InstructionPattern : public Pattern { public: struct Operand { std::string Name; bool IsDef = false; }; InstructionPattern(const CodeGenInstruction &I, StringRef Name) : Pattern(K_Inst, Name), I(I) {} static bool classof(const Pattern *P) { return P->getKind() == K_Inst; } const auto &operands() const { return Operands; } void addOperand(StringRef Name); unsigned getNumDefs() const { return I.Operands.NumDefs; } const CodeGenInstruction &getInst() const { return I; } StringRef getInstName() const { return I.TheDef->getName(); } void reportUnreachable(ArrayRef Locs) const; bool checkSemantics(ArrayRef Loc) const; void print(raw_ostream &OS, bool PrintName = true) const override; private: const CodeGenInstruction &I; SmallVector Operands; }; void InstructionPattern::addOperand(StringRef Name) { const bool IsDef = Operands.size() < getNumDefs(); Operands.emplace_back(Operand{Name.str(), IsDef}); } void InstructionPattern::reportUnreachable(ArrayRef Locs) const { PrintError(Locs, "Instruction pattern '" + getName() + "' is unreachable from the pattern root!"); } bool InstructionPattern::checkSemantics(ArrayRef Loc) const { unsigned NumExpectedOperands = I.Operands.size(); if (NumExpectedOperands != Operands.size()) { PrintError(Loc, "'" + getInstName() + "' expected " + Twine(NumExpectedOperands) + " operands, got " + Twine(Operands.size())); return false; } return true; } void InstructionPattern::print(raw_ostream &OS, bool PrintName) const { printImpl(OS, PrintName, [&OS, this]() { OS << "inst:" << I.TheDef->getName() << " operands:[" << join(map_range(Operands, [](const auto &O) { return (O.IsDef ? "" : "") + O.Name; }), ", ") << "]"; }); } //===- CXXPattern ---------------------------------------------------------===// /// Raw C++ code which may need some expansions. /// /// e.g. [{ return isFooBux(${src}.getReg()); }] /// /// For the expanded code, \see CXXPredicateCode. CXXPredicateCode objects are /// created through `expandCode`. /// /// \see CodeExpander and \see CodeExpansions for more information on code /// expansions. /// /// This object has two purposes: /// - Represent C++ code as a pattern entry. /// - Be a factory for expanded C++ code. /// - It's immutable and only holds the raw code so we can expand the same /// CXX pattern multiple times if we need to. /// /// Note that the code is always trimmed in the constructor, so leading and /// trailing whitespaces are removed. This removes bloat in the output, avoids /// formatting issues, but also allows us to check things like /// `.startswith("return")` trivially without worrying about spaces. class CXXPattern : public Pattern { public: CXXPattern(const StringInit &Code, StringRef Name, bool IsApply) : CXXPattern(Code.getAsUnquotedString(), Name, IsApply) {} CXXPattern(StringRef Code, StringRef Name, bool IsApply) : Pattern(K_CXX, Name), IsApply(IsApply), RawCode(Code.trim().str()) {} static bool classof(const Pattern *P) { return P->getKind() == K_CXX; } bool isApply() const { return IsApply; } StringRef getRawCode() const { return RawCode; } /// Expands raw code, replacing things such as `${foo}` with their /// substitution in \p CE. /// /// \param CE Map of Code Expansions /// \param Locs SMLocs for the Code Expander, in case it needs to emit /// diagnostics. /// \return A CXXPredicateCode object that contains the expanded code. Note /// that this may or may not insert a new object. All CXXPredicateCode objects /// are held in a set to avoid emitting duplicate C++ code. const CXXPredicateCode &expandCode(const CodeExpansions &CE, ArrayRef Locs) const; void print(raw_ostream &OS, bool PrintName = true) const override; private: bool IsApply; std::string RawCode; }; const CXXPredicateCode &CXXPattern::expandCode(const CodeExpansions &CE, ArrayRef Locs) const { std::string Result; raw_string_ostream OS(Result); CodeExpander Expander(RawCode, CE, Locs, /*ShowExpansions*/ false); Expander.emit(OS); return getOrInsert(IsApply ? AllCXXApplyCode : AllCXXMatchCode, std::move(Result)); } void CXXPattern::print(raw_ostream &OS, bool PrintName) const { printImpl(OS, PrintName, [&OS, this] { OS << (IsApply ? "apply" : "match") << " code:\""; printEscapedString(getRawCode(), OS); OS << "\""; }); } //===- CombineRuleBuilder -------------------------------------------------===// /// Helper for CombineRuleBuilder. /// /// Represents information about an operand. /// Operands with no MatchPat are considered live-in to the pattern. struct OperandTableEntry { // The matcher pattern that defines this operand. // null for live-ins. InstructionPattern *MatchPat = nullptr; // The apply pattern that (re)defines this operand. // This can only be non-null if MatchPat is. InstructionPattern *ApplyPat = nullptr; bool isLiveIn() const { return !MatchPat; } }; /// Parses combine rule and builds a small intermediate representation to tie /// patterns together and emit RuleMatchers to match them. This may emit more /// than one RuleMatcher, e.g. for `wip_match_opcode`. /// /// Memory management for `Pattern` objects is done through `std::unique_ptr`. /// In most cases, there are two stages to a pattern's lifetime: /// - Creation in a `parse` function /// - The unique_ptr is stored in a variable, and may be destroyed if the /// pattern is found to be semantically invalid. /// - Ownership transfer into a `PatternMap` /// - Once a pattern is moved into either the map of Match or Apply /// patterns, it is known to be valid and it never moves back. class CombineRuleBuilder { public: using PatternMap = MapVector>; CombineRuleBuilder(const CodeGenTarget &CGT, SubtargetFeatureInfoMap &SubtargetFeatures, Record &RuleDef, unsigned ID, std::vector &OutRMs) : CGT(CGT), SubtargetFeatures(SubtargetFeatures), RuleDef(RuleDef), RuleID(ID), OutRMs(OutRMs) {} /// Parses all fields in the RuleDef record. bool parseAll(); /// Emits all RuleMatchers into the vector of RuleMatchers passed in the /// constructor. bool emitRuleMatchers(); void print(raw_ostream &OS) const; void dump() const { print(dbgs()); } /// Debug-only verification of invariants. void verify() const; private: void PrintError(Twine Msg) const { ::PrintError(RuleDef.getLoc(), Msg); } /// Adds the expansions from \see MatchDatas to \p CE. void declareAllMatchDatasExpansions(CodeExpansions &CE) const; /// Adds \p P to \p IM, expanding its code using \p CE. void addCXXPredicate(InstructionMatcher &IM, const CodeExpansions &CE, const CXXPattern &P); /// Generates a name for anonymous patterns. /// /// e.g. (G_ADD $x, $y, $z):$foo is a pattern named "foo", but if ":$foo" is /// absent, then the pattern is anonymous and this is used to assign it a /// name. std::string makeAnonPatName(StringRef Prefix) const; mutable unsigned AnonIDCnt = 0; /// Creates a new RuleMatcher with some boilerplate /// settings/actions/predicates, and and adds it to \p OutRMs. /// \see addFeaturePredicates too. /// /// \param AdditionalComment Comment string to be added to the /// `DebugCommentAction`. RuleMatcher &addRuleMatcher(Twine AdditionalComment = ""); bool addFeaturePredicates(RuleMatcher &M); bool findRoots(); bool buildOperandsTable(); bool parseDefs(DagInit &Def); bool parseMatch(DagInit &Match); bool parseApply(DagInit &Apply); std::unique_ptr parseInstructionMatcher(const Init &Arg, StringRef PatName); std::unique_ptr parseWipMatchOpcodeMatcher(const Init &Arg, StringRef PatName); bool emitMatchPattern(CodeExpansions &CE, const InstructionPattern &IP); bool emitMatchPattern(CodeExpansions &CE, const AnyOpcodePattern &AOP); bool emitApplyPatterns(CodeExpansions &CE, RuleMatcher &M); // Recursively visits InstructionPattern from P to build up the // RuleMatcher/InstructionMatcher. May create new InstructionMatchers as // needed. bool emitInstructionMatchPattern(CodeExpansions &CE, RuleMatcher &M, InstructionMatcher &IM, const InstructionPattern &P, DenseSet &SeenPats); const CodeGenTarget &CGT; SubtargetFeatureInfoMap &SubtargetFeatures; Record &RuleDef; const unsigned RuleID; std::vector &OutRMs; // For InstructionMatcher::addOperand unsigned AllocatedTemporariesBaseID = 0; /// The root of the pattern. StringRef RootName; /// These maps have ownership of the actual Pattern objects. /// They both map a Pattern's name to the Pattern instance. PatternMap MatchPats; PatternMap ApplyPats; /// Set by findRoots. Pattern *MatchRoot = nullptr; MapVector OperandTable; SmallVector MatchDatas; }; bool CombineRuleBuilder::parseAll() { if (!parseDefs(*RuleDef.getValueAsDag("Defs"))) return false; if (!parseMatch(*RuleDef.getValueAsDag("Match"))) return false; if (!parseApply(*RuleDef.getValueAsDag("Apply"))) return false; if (!buildOperandsTable()) return false; if (!findRoots()) return false; LLVM_DEBUG(verify()); return true; } bool CombineRuleBuilder::emitRuleMatchers() { assert(MatchRoot); CodeExpansions CE; declareAllMatchDatasExpansions(CE); switch (MatchRoot->getKind()) { case Pattern::K_AnyOpcode: { if (!emitMatchPattern(CE, *cast(MatchRoot))) return false; break; } case Pattern::K_Inst: if (!emitMatchPattern(CE, *cast(MatchRoot))) return false; break; case Pattern::K_CXX: PrintError("C++ code cannot be the root of a pattern!"); return false; default: llvm_unreachable("unknown pattern kind!"); } return true; } void CombineRuleBuilder::print(raw_ostream &OS) const { OS << "(CombineRule name:" << RuleDef.getName() << " id:" << RuleID << " root:" << RootName << "\n"; OS << " (MatchDatas "; if (MatchDatas.empty()) OS << ")\n"; else { OS << "\n"; for (const auto &MD : MatchDatas) { OS << " "; MD.print(OS); OS << "\n"; } OS << " )\n"; } const auto DumpPats = [&](StringRef Name, const PatternMap &Pats) { OS << " (" << Name << " "; if (Pats.empty()) { OS << ")\n"; return; } OS << "\n"; for (const auto &[Name, Pat] : Pats) { OS << " "; if (Pat.get() == MatchRoot) OS << ""; OS << Name << ":"; Pat->print(OS, /*PrintName=*/false); OS << "\n"; } OS << " )\n"; }; DumpPats("MatchPats", MatchPats); DumpPats("ApplyPats", ApplyPats); OS << " (OperandTable "; if (OperandTable.empty()) OS << ")\n"; else { OS << "\n"; for (const auto &[Key, Val] : OperandTable) { OS << " [" << Key; if (const auto *P = Val.MatchPat) OS << " match_pat:" << P->getName(); if (const auto *P = Val.ApplyPat) OS << " apply_pat:" << P->getName(); if (Val.isLiveIn()) OS << " live-in"; OS << "]\n"; } OS << " )\n"; } OS << ")\n"; } void CombineRuleBuilder::verify() const { const auto VerifyPats = [&](const PatternMap &Pats) { for (const auto &[Name, Pat] : Pats) { if (!Pat) PrintFatalError("null pattern in pattern map!"); if (Name != Pat->getName()) { Pat->dump(); PrintFatalError("Pattern name mismatch! Map name: " + Name + ", Pat name: " + Pat->getName()); } // As an optimization, the PatternMaps don't re-allocate the PatternName // string. They simply reference the std::string inside Pattern. Ensure // this is the case to avoid memory issues. if (Name.data() != Pat->getName().data()) { dbgs() << "Map StringRef: '" << Name << "' @ " << (const void *)Name.data() << "\n"; dbgs() << "Pat String: '" << Pat->getName() << "' @ " << (const void *)Pat->getName().data() << "\n"; PrintFatalError("StringRef stored in the PatternMap is not referencing " "the same string as its Pattern!"); } } }; VerifyPats(MatchPats); VerifyPats(ApplyPats); for (const auto &[Name, Op] : OperandTable) { if (Op.ApplyPat && !Op.MatchPat) { dump(); PrintFatalError("Operand " + Name + " has an apply pattern, but no match pattern!"); } } } bool CombineRuleBuilder::addFeaturePredicates(RuleMatcher &M) { if (!RuleDef.getValue("Predicates")) return true; ListInit *Preds = RuleDef.getValueAsListInit("Predicates"); for (Init *I : Preds->getValues()) { if (DefInit *Pred = dyn_cast(I)) { Record *Def = Pred->getDef(); if (!Def->isSubClassOf("Predicate")) { ::PrintError(Def->getLoc(), "Unknown 'Predicate' Type"); return false; } if (Def->getValueAsString("CondString").empty()) continue; if (SubtargetFeatures.count(Def) == 0) { SubtargetFeatures.emplace( Def, SubtargetFeatureInfo(Def, SubtargetFeatures.size())); } M.addRequiredFeature(Def); } } return true; } void CombineRuleBuilder::declareAllMatchDatasExpansions( CodeExpansions &CE) const { for (const auto &MD : MatchDatas) CE.declare(MD.getPatternSymbol(), MD.getQualifiedVariableName()); } void CombineRuleBuilder::addCXXPredicate(InstructionMatcher &IM, const CodeExpansions &CE, const CXXPattern &P) { const auto &ExpandedCode = P.expandCode(CE, RuleDef.getLoc()); IM.addPredicate( ExpandedCode.getEnumNameWithPrefix(CXXPredPrefix)); } std::string CombineRuleBuilder::makeAnonPatName(StringRef Prefix) const { return to_string("__anon_pat_" + Prefix + "_" + to_string(RuleID) + "_" + to_string(AnonIDCnt++)); } RuleMatcher &CombineRuleBuilder::addRuleMatcher(Twine AdditionalComment) { auto &RM = OutRMs.emplace_back(RuleDef.getLoc()); addFeaturePredicates(RM); RM.addRequiredSimplePredicate(getIsEnabledPredicateEnumName(RuleID)); const std::string AdditionalCommentStr = AdditionalComment.str(); RM.addAction( "Combiner Rule #" + to_string(RuleID) + ": " + RuleDef.getName().str() + (AdditionalCommentStr.empty() ? "" : "; " + AdditionalCommentStr)); return RM; } bool CombineRuleBuilder::findRoots() { // Look by pattern name, e.g. // (G_FNEG $x, $y):$root if (auto It = MatchPats.find(RootName); It != MatchPats.end()) { MatchRoot = It->second.get(); return true; } // Look by def: // (G_FNEG $root, $y) auto It = OperandTable.find(RootName); if (It == OperandTable.end()) { PrintError("Cannot find root '" + RootName + "' in match patterns!"); return false; } if (!It->second.MatchPat) { PrintError("Cannot use live-in operand '" + RootName + "' as match pattern root!"); return false; } MatchRoot = It->second.MatchPat; return true; } bool CombineRuleBuilder::buildOperandsTable() { // Walk each instruction pattern for (auto &P : values(MatchPats)) { auto *IP = dyn_cast(P.get()); if (!IP) continue; for (const auto &Operand : IP->operands()) { // Create an entry, no matter if it's a use or a def. auto &Entry = OperandTable[Operand.Name]; // We only need to do additional checking on defs, though. if (!Operand.IsDef) continue; if (Entry.MatchPat) { PrintError("Operand '" + Operand.Name + "' is defined multiple times in the 'match' patterns"); return false; } Entry.MatchPat = IP; } } for (auto &P : values(ApplyPats)) { auto *IP = dyn_cast(P.get()); if (!IP) continue; for (const auto &Operand : IP->operands()) { // Create an entry, no matter if it's a use or a def. auto &Entry = OperandTable[Operand.Name]; // We only need to do additional checking on defs, though. if (!Operand.IsDef) continue; if (!Entry.MatchPat) { PrintError("Cannot define live-in operand '" + Operand.Name + "' in the 'apply' pattern"); return false; } if (Entry.ApplyPat) { PrintError("Operand '" + Operand.Name + "' is defined multiple times in the 'apply' patterns"); return false; } Entry.ApplyPat = IP; } } return true; } bool CombineRuleBuilder::parseDefs(DagInit &Def) { if (Def.getOperatorAsDef(RuleDef.getLoc())->getName() != "defs") { PrintError("Expected defs operator"); return false; } SmallVector Roots; for (unsigned I = 0, E = Def.getNumArgs(); I < E; ++I) { if (isSpecificDef(*Def.getArg(I), "root")) { Roots.emplace_back(Def.getArgNameStr(I)); continue; } // Subclasses of GIDefMatchData should declare that this rule needs to pass // data from the match stage to the apply stage, and ensure that the // generated matcher has a suitable variable for it to do so. if (Record *MatchDataRec = getDefOfSubClass(*Def.getArg(I), "GIDefMatchData")) { MatchDatas.emplace_back(Def.getArgNameStr(I), MatchDataRec->getValueAsString("Type")); continue; } // Otherwise emit an appropriate error message. if (getDefOfSubClass(*Def.getArg(I), "GIDefKind")) PrintError("This GIDefKind not implemented in tablegen"); else if (getDefOfSubClass(*Def.getArg(I), "GIDefKindWithArgs")) PrintError("This GIDefKindWithArgs not implemented in tablegen"); else PrintError("Expected a subclass of GIDefKind or a sub-dag whose " "operator is of type GIDefKindWithArgs"); return false; } if (Roots.size() != 1) { PrintError("Combine rules must have exactly one root"); return false; } RootName = Roots.front(); // Assign variables to all MatchDatas. AssignMatchDataVariables(MatchDatas); return true; } bool CombineRuleBuilder::parseMatch(DagInit &Match) { if (Match.getOperatorAsDef(RuleDef.getLoc())->getName() != "match") { PrintError("Expected match operator"); return false; } if (Match.getNumArgs() == 0) { PrintError("Matcher is empty"); return false; } // The match section consists of a list of matchers and predicates. Parse each // one and add the equivalent GIMatchDag nodes, predicates, and edges. bool HasOpcodeMatcher = false; for (unsigned I = 0; I < Match.getNumArgs(); ++I) { Init *Arg = Match.getArg(I); std::string Name = Match.getArgName(I) ? Match.getArgName(I)->getValue().str() : makeAnonPatName("match"); if (MatchPats.contains(Name)) { PrintError("'" + Name + "' match pattern defined more than once!"); return false; } if (auto Pat = parseInstructionMatcher(*Arg, Name)) { MatchPats[Pat->getName()] = std::move(Pat); continue; } if (auto Pat = parseWipMatchOpcodeMatcher(*Arg, Name)) { if (HasOpcodeMatcher) { PrintError("wip_opcode_match can only be present once"); return false; } HasOpcodeMatcher = true; MatchPats[Pat->getName()] = std::move(Pat); continue; } // Parse arbitrary C++ code if (const auto *StringI = dyn_cast(Arg)) { auto CXXPat = std::make_unique(*StringI, Name, /*IsApply*/ false); if (!CXXPat->getRawCode().contains("return ")) { PrintWarning(RuleDef.getLoc(), "'match' C++ code does not seem to return!"); } MatchPats[CXXPat->getName()] = std::move(CXXPat); continue; } // TODO: don't print this on, e.g. bad operand count in inst pat PrintError("Expected a subclass of GIMatchKind or a sub-dag whose " "operator is either of a GIMatchKindWithArgs or Instruction"); PrintNote("Pattern was `" + Arg->getAsString() + "'"); return false; } return true; } bool CombineRuleBuilder::parseApply(DagInit &Apply) { // Currently we only support C++ :( if (Apply.getOperatorAsDef(RuleDef.getLoc())->getName() != "apply") { PrintError("Expected 'apply' operator in Apply DAG"); return false; } if (Apply.getNumArgs() != 1) { PrintError("Expected exactly 1 argument in 'apply'"); return false; } const StringInit *Code = dyn_cast(Apply.getArg(0)); auto Pat = std::make_unique(*Code, makeAnonPatName("apply"), /*IsApply*/ true); ApplyPats[Pat->getName()] = std::move(Pat); return true; } std::unique_ptr CombineRuleBuilder::parseInstructionMatcher(const Init &Arg, StringRef Name) { const DagInit *Matcher = getDagWithOperatorOfSubClass(Arg, "Instruction"); if (!Matcher) return nullptr; auto &Instr = CGT.getInstruction(Matcher->getOperatorAsDef(RuleDef.getLoc())); auto Pat = std::make_unique(Instr, Name); for (const auto &NameInit : Matcher->getArgNames()) Pat->addOperand(NameInit->getAsUnquotedString()); if (!Pat->checkSemantics(RuleDef.getLoc())) return nullptr; return std::move(Pat); } std::unique_ptr CombineRuleBuilder::parseWipMatchOpcodeMatcher(const Init &Arg, StringRef Name) { const DagInit *Matcher = getDagWithSpecificOperator(Arg, "wip_match_opcode"); if (!Matcher) return nullptr; if (Matcher->getNumArgs() == 0) { PrintError("Empty wip_match_opcode"); return nullptr; } // Each argument is an opcode that can match. auto Result = std::make_unique(Name); for (const auto &Arg : Matcher->getArgs()) { Record *OpcodeDef = getDefOfSubClass(*Arg, "Instruction"); if (OpcodeDef) { Result->addOpcode(&CGT.getInstruction(OpcodeDef)); continue; } PrintError("Arguments to wip_match_opcode must be instructions"); return nullptr; } return std::move(Result); } bool CombineRuleBuilder::emitMatchPattern(CodeExpansions &CE, const InstructionPattern &IP) { auto &M = addRuleMatcher(); InstructionMatcher &IM = M.addInstructionMatcher("root"); declareInstExpansion(CE, IM, IP.getName()); DenseSet SeenPats; if (!emitInstructionMatchPattern(CE, M, IM, IP, SeenPats)) return false; // Emit remaining patterns for (auto &Pat : values(MatchPats)) { if (SeenPats.contains(Pat.get())) continue; switch (Pat->getKind()) { case Pattern::K_AnyOpcode: PrintError("wip_match_opcode can not be used with instruction patterns!"); return false; case Pattern::K_Inst: cast(Pat.get())->reportUnreachable(RuleDef.getLoc()); return false; case Pattern::K_CXX: { addCXXPredicate(IM, CE, *cast(Pat.get())); continue; } default: llvm_unreachable("unknown pattern kind!"); } } return emitApplyPatterns(CE, M); } bool CombineRuleBuilder::emitMatchPattern(CodeExpansions &CE, const AnyOpcodePattern &AOP) { for (const CodeGenInstruction *CGI : AOP.insts()) { auto &M = addRuleMatcher("wip_match_opcode alternative '" + CGI->TheDef->getName() + "'"); InstructionMatcher &IM = M.addInstructionMatcher(AOP.getName()); declareInstExpansion(CE, IM, AOP.getName()); // declareInstExpansion needs to be identical, otherwise we need to create a // CodeExpansions object here instead. assert(IM.getInsnVarID() == 0); IM.addPredicate(CGI); // Emit remaining patterns. for (auto &Pat : values(MatchPats)) { if (Pat.get() == &AOP) continue; switch (Pat->getKind()) { case Pattern::K_AnyOpcode: PrintError("wip_match_opcode can only be present once!"); return false; case Pattern::K_Inst: cast(Pat.get())->reportUnreachable( RuleDef.getLoc()); return false; case Pattern::K_CXX: { addCXXPredicate(IM, CE, *cast(Pat.get())); break; } default: llvm_unreachable("unknown pattern kind!"); } } if (!emitApplyPatterns(CE, M)) return false; } return true; } bool CombineRuleBuilder::emitApplyPatterns(CodeExpansions &CE, RuleMatcher &M) { for (auto &Pat : values(ApplyPats)) { switch (Pat->getKind()) { case Pattern::K_AnyOpcode: case Pattern::K_Inst: llvm_unreachable("Unsupported pattern kind in output pattern!"); case Pattern::K_CXX: { CXXPattern *CXXPat = cast(Pat.get()); const auto &ExpandedCode = CXXPat->expandCode(CE, RuleDef.getLoc()); M.addAction( ExpandedCode.getEnumNameWithPrefix(CXXApplyPrefix)); continue; } default: llvm_unreachable("Unknown pattern kind!"); } } return true; } bool CombineRuleBuilder::emitInstructionMatchPattern( CodeExpansions &CE, RuleMatcher &M, InstructionMatcher &IM, const InstructionPattern &P, DenseSet &SeenPats) { if (SeenPats.contains(&P)) return true; SeenPats.insert(&P); IM.addPredicate(&P.getInst()); declareInstExpansion(CE, IM, P.getName()); unsigned OpIdx = 0; for (auto &O : P.operands()) { auto &OpTableEntry = OperandTable.find(O.Name)->second; OperandMatcher &OM = IM.addOperand(OpIdx++, O.Name, AllocatedTemporariesBaseID++); declareOperandExpansion(CE, OM, O.Name); if (O.IsDef) continue; if (InstructionPattern *DefPat = OpTableEntry.MatchPat) { auto InstOpM = OM.addPredicate(M, O.Name); if (!InstOpM) { // TODO: copy-pasted from GlobalISelEmitter.cpp. Is it still relevant // here? PrintError("Nested instruction '" + DefPat->getName() + "' cannot be the same as another operand '" + O.Name + "'"); return false; } if (!emitInstructionMatchPattern(CE, M, (*InstOpM)->getInsnMatcher(), *DefPat, SeenPats)) return false; } } return true; } //===- GICombinerEmitter --------------------------------------------------===// /// This class is essentially the driver. It fetches all TableGen records, calls /// CombineRuleBuilder to build the MatchTable's RuleMatchers, then creates the /// MatchTable & emits it. It also handles emitting all the supporting code such /// as the list of LLTs, the CXXPredicates, etc. class GICombinerEmitter final : public GlobalISelMatchTableExecutorEmitter { RecordKeeper &Records; StringRef Name; const CodeGenTarget &Target; Record *Combiner; unsigned NextRuleID = 0; // List all combine rules (ID, name) imported. // Note that the combiner rule ID is different from the RuleMatcher ID. The // latter is internal to the MatchTable, the former is the canonical ID of the // combine rule used to disable/enable it. std::vector> AllCombineRules; MatchTable buildMatchTable(MutableArrayRef Rules); void emitRuleConfigImpl(raw_ostream &OS); void emitAdditionalImpl(raw_ostream &OS) override; void emitMIPredicateFns(raw_ostream &OS) override; void emitI64ImmPredicateFns(raw_ostream &OS) override; void emitAPFloatImmPredicateFns(raw_ostream &OS) override; void emitAPIntImmPredicateFns(raw_ostream &OS) override; void emitTestSimplePredicate(raw_ostream &OS) override; void emitRunCustomAction(raw_ostream &OS) override; void emitAdditionalTemporariesDecl(raw_ostream &OS, StringRef Indent) override; const CodeGenTarget &getTarget() const override { return Target; } StringRef getClassName() const override { return Combiner->getValueAsString("Classname"); } std::string getRuleConfigClassName() const { return getClassName().str() + "RuleConfig"; } void gatherRules(std::vector &Rules, const std::vector &&RulesAndGroups); public: explicit GICombinerEmitter(RecordKeeper &RK, const CodeGenTarget &Target, StringRef Name, Record *Combiner); ~GICombinerEmitter() {} void run(raw_ostream &OS); }; void GICombinerEmitter::emitRuleConfigImpl(raw_ostream &OS) { OS << "struct " << getRuleConfigClassName() << " {\n" << " SparseBitVector<> DisabledRules;\n\n" << " bool isRuleEnabled(unsigned RuleID) const;\n" << " bool parseCommandLineOption();\n" << " bool setRuleEnabled(StringRef RuleIdentifier);\n" << " bool setRuleDisabled(StringRef RuleIdentifier);\n" << "};\n\n"; std::vector> Cases; Cases.reserve(AllCombineRules.size()); for (const auto &[ID, Name] : AllCombineRules) Cases.emplace_back(Name, "return " + to_string(ID) + ";\n"); OS << "static std::optional getRuleIdxForIdentifier(StringRef " "RuleIdentifier) {\n" << " uint64_t I;\n" << " // getAtInteger(...) returns false on success\n" << " bool Parsed = !RuleIdentifier.getAsInteger(0, I);\n" << " if (Parsed)\n" << " return I;\n\n" << "#ifndef NDEBUG\n"; StringMatcher Matcher("RuleIdentifier", Cases, OS); Matcher.Emit(); OS << "#endif // ifndef NDEBUG\n\n" << " return std::nullopt;\n" << "}\n"; OS << "static std::optional> " "getRuleRangeForIdentifier(StringRef RuleIdentifier) {\n" << " std::pair RangePair = " "RuleIdentifier.split('-');\n" << " if (!RangePair.second.empty()) {\n" << " const auto First = " "getRuleIdxForIdentifier(RangePair.first);\n" << " const auto Last = " "getRuleIdxForIdentifier(RangePair.second);\n" << " if (!First || !Last)\n" << " return std::nullopt;\n" << " if (First >= Last)\n" << " report_fatal_error(\"Beginning of range should be before " "end of range\");\n" << " return {{*First, *Last + 1}};\n" << " }\n" << " if (RangePair.first == \"*\") {\n" << " return {{0, " << AllCombineRules.size() << "}};\n" << " }\n" << " const auto I = getRuleIdxForIdentifier(RangePair.first);\n" << " if (!I)\n" << " return std::nullopt;\n" << " return {{*I, *I + 1}};\n" << "}\n\n"; for (bool Enabled : {true, false}) { OS << "bool " << getRuleConfigClassName() << "::setRule" << (Enabled ? "Enabled" : "Disabled") << "(StringRef RuleIdentifier) {\n" << " auto MaybeRange = getRuleRangeForIdentifier(RuleIdentifier);\n" << " if (!MaybeRange)\n" << " return false;\n" << " for (auto I = MaybeRange->first; I < MaybeRange->second; ++I)\n" << " DisabledRules." << (Enabled ? "reset" : "set") << "(I);\n" << " return true;\n" << "}\n\n"; } OS << "static std::vector " << Name << "Option;\n" << "static cl::list " << Name << "DisableOption(\n" << " \"" << Name.lower() << "-disable-rule\",\n" << " cl::desc(\"Disable one or more combiner rules temporarily in " << "the " << Name << " pass\"),\n" << " cl::CommaSeparated,\n" << " cl::Hidden,\n" << " cl::cat(GICombinerOptionCategory),\n" << " cl::callback([](const std::string &Str) {\n" << " " << Name << "Option.push_back(Str);\n" << " }));\n" << "static cl::list " << Name << "OnlyEnableOption(\n" << " \"" << Name.lower() << "-only-enable-rule\",\n" << " cl::desc(\"Disable all rules in the " << Name << " pass then re-enable the specified ones\"),\n" << " cl::Hidden,\n" << " cl::cat(GICombinerOptionCategory),\n" << " cl::callback([](const std::string &CommaSeparatedArg) {\n" << " StringRef Str = CommaSeparatedArg;\n" << " " << Name << "Option.push_back(\"*\");\n" << " do {\n" << " auto X = Str.split(\",\");\n" << " " << Name << "Option.push_back((\"!\" + X.first).str());\n" << " Str = X.second;\n" << " } while (!Str.empty());\n" << " }));\n" << "\n\n" << "bool " << getRuleConfigClassName() << "::isRuleEnabled(unsigned RuleID) const {\n" << " return !DisabledRules.test(RuleID);\n" << "}\n" << "bool " << getRuleConfigClassName() << "::parseCommandLineOption() {\n" << " for (StringRef Identifier : " << Name << "Option) {\n" << " bool Enabled = Identifier.consume_front(\"!\");\n" << " if (Enabled && !setRuleEnabled(Identifier))\n" << " return false;\n" << " if (!Enabled && !setRuleDisabled(Identifier))\n" << " return false;\n" << " }\n" << " return true;\n" << "}\n\n"; } void GICombinerEmitter::emitAdditionalImpl(raw_ostream &OS) { OS << "bool " << getClassName() << "::tryCombineAll(MachineInstr &I) const {\n" << " const TargetSubtargetInfo &ST = MF.getSubtarget();\n" << " const PredicateBitset AvailableFeatures = " "getAvailableFeatures();\n" << " NewMIVector OutMIs;\n" << " State.MIs.clear();\n" << " State.MIs.push_back(&I);\n" << " " << MatchDataInfo::StructName << " = " << MatchDataInfo::StructTypeName << "();\n\n" << " if (executeMatchTable(*this, OutMIs, State, ExecInfo" << ", getMatchTable(), *ST.getInstrInfo(), MRI, " "*MRI.getTargetRegisterInfo(), *ST.getRegBankInfo(), AvailableFeatures" << ", /*CoverageInfo*/ nullptr)) {\n" << " return true;\n" << " }\n\n" << " return false;\n" << "}\n\n"; } void GICombinerEmitter::emitMIPredicateFns(raw_ostream &OS) { auto MatchCode = getSorted(AllCXXMatchCode); emitMIPredicateFnsImpl( OS, "", ArrayRef(MatchCode), [](const CXXPredicateCode *C) -> StringRef { return C->BaseEnumName; }, [](const CXXPredicateCode *C) -> StringRef { return C->Code; }); } void GICombinerEmitter::emitI64ImmPredicateFns(raw_ostream &OS) { // Unused, but still needs to be called. emitImmPredicateFnsImpl( OS, "I64", "int64_t", {}, [](unsigned) { return ""; }, [](unsigned) { return ""; }); } void GICombinerEmitter::emitAPFloatImmPredicateFns(raw_ostream &OS) { // Unused, but still needs to be called. emitImmPredicateFnsImpl( OS, "APFloat", "const APFloat &", {}, [](unsigned) { return ""; }, [](unsigned) { return ""; }); } void GICombinerEmitter::emitAPIntImmPredicateFns(raw_ostream &OS) { // Unused, but still needs to be called. emitImmPredicateFnsImpl( OS, "APInt", "const APInt &", {}, [](unsigned) { return ""; }, [](unsigned) { return ""; }); } void GICombinerEmitter::emitTestSimplePredicate(raw_ostream &OS) { if (!AllCombineRules.empty()) { OS << "enum {\n"; std::string EnumeratorSeparator = " = GICXXPred_Invalid + 1,\n"; // To avoid emitting a switch, we expect that all those rules are in order. // That way we can just get the RuleID from the enum by subtracting // (GICXXPred_Invalid + 1). unsigned ExpectedID = 0; (void)ExpectedID; for (const auto &ID : keys(AllCombineRules)) { assert(ExpectedID++ == ID && "combine rules are not ordered!"); OS << " " << getIsEnabledPredicateEnumName(ID) << EnumeratorSeparator; EnumeratorSeparator = ",\n"; } OS << "};\n\n"; } OS << "bool " << getClassName() << "::testSimplePredicate(unsigned Predicate) const {\n" << " return RuleConfig.isRuleEnabled(Predicate - " "GICXXPred_Invalid - " "1);\n" << "}\n"; } void GICombinerEmitter::emitRunCustomAction(raw_ostream &OS) { const auto ApplyCode = getSorted(AllCXXApplyCode); if (!ApplyCode.empty()) { OS << "enum {\n"; std::string EnumeratorSeparator = " = GICXXCustomAction_Invalid + 1,\n"; for (const auto &Apply : ApplyCode) { OS << " " << Apply->getEnumNameWithPrefix(CXXApplyPrefix) << EnumeratorSeparator; EnumeratorSeparator = ",\n"; } OS << "};\n"; } OS << "void " << getClassName() << "::runCustomAction(unsigned ApplyID, const MatcherState &State) const " "{\n"; if (!ApplyCode.empty()) { OS << " switch(ApplyID) {\n"; for (const auto &Apply : ApplyCode) { OS << " case " << Apply->getEnumNameWithPrefix(CXXApplyPrefix) << ":{\n" << " " << Apply->Code << "\n" << " return;\n"; OS << " }\n"; } OS << "}\n"; } OS << " llvm_unreachable(\"Unknown Apply Action\");\n" << "}\n"; } void GICombinerEmitter::emitAdditionalTemporariesDecl(raw_ostream &OS, StringRef Indent) { OS << Indent << "struct " << MatchDataInfo::StructTypeName << " {\n"; for (const auto &[Type, VarNames] : AllMatchDataVars) { assert(!VarNames.empty() && "Cannot have no vars for this type!"); OS << Indent << " " << Type << " " << join(VarNames, ", ") << ";\n"; } OS << Indent << "};\n" << Indent << "mutable " << MatchDataInfo::StructTypeName << " " << MatchDataInfo::StructName << ";\n\n"; } GICombinerEmitter::GICombinerEmitter(RecordKeeper &RK, const CodeGenTarget &Target, StringRef Name, Record *Combiner) : Records(RK), Name(Name), Target(Target), Combiner(Combiner) {} MatchTable GICombinerEmitter::buildMatchTable(MutableArrayRef Rules) { std::vector InputRules; for (Matcher &Rule : Rules) InputRules.push_back(&Rule); unsigned CurrentOrdering = 0; StringMap OpcodeOrder; for (RuleMatcher &Rule : Rules) { const StringRef Opcode = Rule.getOpcode(); assert(!Opcode.empty() && "Didn't expect an undefined opcode"); if (OpcodeOrder.count(Opcode) == 0) OpcodeOrder[Opcode] = CurrentOrdering++; } llvm::stable_sort(InputRules, [&OpcodeOrder](const Matcher *A, const Matcher *B) { auto *L = static_cast(A); auto *R = static_cast(B); return std::make_tuple(OpcodeOrder[L->getOpcode()], L->getNumOperands()) < std::make_tuple(OpcodeOrder[R->getOpcode()], R->getNumOperands()); }); for (Matcher *Rule : InputRules) Rule->optimize(); std::vector> MatcherStorage; std::vector OptRules = optimizeRules(InputRules, MatcherStorage); for (Matcher *Rule : OptRules) Rule->optimize(); OptRules = optimizeRules(OptRules, MatcherStorage); return MatchTable::buildTable(OptRules, /*WithCoverage*/ false, /*IsCombiner*/ true); } /// Recurse into GICombineGroup's and flatten the ruleset into a simple list. void GICombinerEmitter::gatherRules( std::vector &ActiveRules, const std::vector &&RulesAndGroups) { for (Record *R : RulesAndGroups) { if (R->isValueUnset("Rules")) { AllCombineRules.emplace_back(NextRuleID, R->getName().str()); CombineRuleBuilder CRB(Target, SubtargetFeatures, *R, NextRuleID++, ActiveRules); if (!CRB.parseAll()) continue; if (StopAfterParse) { CRB.print(outs()); continue; } if (!CRB.emitRuleMatchers()) continue; } else gatherRules(ActiveRules, R->getValueAsListOfDefs("Rules")); } } void GICombinerEmitter::run(raw_ostream &OS) { Records.startTimer("Gather rules"); std::vector Rules; gatherRules(Rules, Combiner->getValueAsListOfDefs("Rules")); if (ErrorsPrinted) PrintFatalError(Combiner->getLoc(), "Failed to parse one or more rules"); Records.startTimer("Creating Match Table"); unsigned MaxTemporaries = 0; for (const auto &Rule : Rules) MaxTemporaries = std::max(MaxTemporaries, Rule.countRendererFns()); const MatchTable Table = buildMatchTable(Rules); Records.startTimer("Emit combiner"); emitSourceFileHeader(getClassName().str() + " Combiner Match Table", OS); // Unused std::vector CustomRendererFns; // Unused, but hack to avoid empty declarator std::vector TypeObjects = {LLTCodeGen(LLT::scalar(1))}; // Unused std::vector ComplexPredicates; // GET_GICOMBINER_DEPS, which pulls in extra dependencies. OS << "#ifdef GET_GICOMBINER_DEPS\n" << "#include \"llvm/ADT/SparseBitVector.h\"\n" << "namespace llvm {\n" << "extern cl::OptionCategory GICombinerOptionCategory;\n" << "} // end namespace llvm\n" << "#endif // ifdef GET_GICOMBINER_DEPS\n\n"; // GET_GICOMBINER_TYPES, which needs to be included before the declaration of // the class. OS << "#ifdef GET_GICOMBINER_TYPES\n"; emitRuleConfigImpl(OS); OS << "#endif // ifdef GET_GICOMBINER_TYPES\n\n"; emitPredicateBitset(OS, "GET_GICOMBINER_TYPES"); // GET_GICOMBINER_CLASS_MEMBERS, which need to be included inside the class. emitPredicatesDecl(OS, "GET_GICOMBINER_CLASS_MEMBERS"); emitTemporariesDecl(OS, "GET_GICOMBINER_CLASS_MEMBERS"); // GET_GICOMBINER_IMPL, which needs to be included outside the class. emitExecutorImpl(OS, Table, TypeObjects, Rules, ComplexPredicates, CustomRendererFns, "GET_GICOMBINER_IMPL"); // GET_GICOMBINER_CONSTRUCTOR_INITS, which are in the constructor's // initializer list. emitPredicatesInit(OS, "GET_GICOMBINER_CONSTRUCTOR_INITS"); emitTemporariesInit(OS, MaxTemporaries, "GET_GICOMBINER_CONSTRUCTOR_INITS"); } } // end anonymous namespace //===----------------------------------------------------------------------===// static void EmitGICombiner(RecordKeeper &RK, raw_ostream &OS) { CodeGenTarget Target(RK); if (SelectedCombiners.empty()) PrintFatalError("No combiners selected with -combiners"); for (const auto &Combiner : SelectedCombiners) { Record *CombinerDef = RK.getDef(Combiner); if (!CombinerDef) PrintFatalError("Could not find " + Combiner); GICombinerEmitter(RK, Target, Combiner, CombinerDef).run(OS); } } static TableGen::Emitter::Opt X("gen-global-isel-combiner-matchtable", EmitGICombiner, "Generate GlobalISel combiner Match Table");