summaryrefslogtreecommitdiff
path: root/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp')
-rw-r--r--llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp3041
1 files changed, 3041 insertions, 0 deletions
diff --git a/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp b/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp
new file mode 100644
index 000000000000..89aca87a28ec
--- /dev/null
+++ b/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp
@@ -0,0 +1,3041 @@
+//===- 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.
+///
+/// Usually, TableGen backends use "assert is an error" as a means to report
+/// invalid input. They try to diagnose common case but don't try very hard and
+/// crashes can be common. This backend aims to behave closer to how a language
+/// compiler frontend would behave: we try extra hard to diagnose invalid inputs
+/// early, and any crash should be considered a bug (= a feature or diagnostic
+/// is missing).
+///
+/// While this can make the backend a bit more complex than it needs to be, it
+/// pays off because MIR patterns can get complicated. Giving useful error
+/// messages to combine writers can help boost their productivity.
+///
+/// As with anything, a good balance has to be found. We also don't want to
+/// write hundreds of lines of code to detect edge cases. In practice, crashing
+/// very occasionally, or giving poor errors in some rare instances, is fine.
+///
+//===----------------------------------------------------------------------===//
+
+#include "CodeGenInstruction.h"
+#include "CodeGenTarget.h"
+#include "GlobalISel/CXXPredicates.h"
+#include "GlobalISel/CodeExpander.h"
+#include "GlobalISel/CodeExpansions.h"
+#include "GlobalISel/CombinerUtils.h"
+#include "GlobalISel/MatchDataInfo.h"
+#include "GlobalISel/Patterns.h"
+#include "GlobalISelMatchTable.h"
+#include "GlobalISelMatchTableExecutorEmitter.h"
+#include "SubtargetFeatureInfo.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/EquivalenceClasses.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/PrettyStackTrace.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 <cstdint>
+
+using namespace llvm;
+using namespace llvm::gi;
+
+#define DEBUG_TYPE "gicombiner-emitter"
+
+namespace {
+cl::OptionCategory
+ GICombinerEmitterCat("Options for -gen-global-isel-combiner");
+cl::opt<bool> StopAfterParse(
+ "gicombiner-stop-after-parse",
+ cl::desc("Stop processing after parsing rules and dump state"),
+ cl::cat(GICombinerEmitterCat));
+cl::list<std::string>
+ SelectedCombiners("combiners", cl::desc("Emit the specified combiners"),
+ cl::cat(GICombinerEmitterCat), cl::CommaSeparated);
+cl::opt<bool> DebugCXXPreds(
+ "gicombiner-debug-cxxpreds",
+ cl::desc("Add Contextual/Debug comments to all C++ predicates"),
+ cl::cat(GICombinerEmitterCat));
+cl::opt<bool> DebugTypeInfer("gicombiner-debug-typeinfer",
+ cl::desc("Print type inference debug logs"),
+ cl::cat(GICombinerEmitterCat));
+
+constexpr StringLiteral CXXApplyPrefix = "GICXXCustomAction_CombineApply";
+constexpr StringLiteral CXXPredPrefix = "GICXXPred_MI_Predicate_";
+constexpr StringLiteral MIFlagsEnumClassName = "MIFlagEnum";
+
+//===- CodeExpansions Helpers --------------------------------------------===//
+
+void declareInstExpansion(CodeExpansions &CE, const InstructionMatcher &IM,
+ StringRef Name) {
+ CE.declare(Name, "State.MIs[" + to_string(IM.getInsnVarID()) + "]");
+}
+
+void declareInstExpansion(CodeExpansions &CE, const BuildMIAction &A,
+ StringRef Name) {
+ // Note: we use redeclare here because this may overwrite a matcher inst
+ // expansion.
+ CE.redeclare(Name, "OutMIs[" + to_string(A.getInsnID()) + "]");
+}
+
+void declareOperandExpansion(CodeExpansions &CE, const OperandMatcher &OM,
+ StringRef Name) {
+ CE.declare(Name, "State.MIs[" + to_string(OM.getInsnVarID()) +
+ "]->getOperand(" + to_string(OM.getOpIdx()) + ")");
+}
+
+void declareTempRegExpansion(CodeExpansions &CE, unsigned TempRegID,
+ StringRef Name) {
+ CE.declare(Name, "State.TempRegisters[" + to_string(TempRegID) + "]");
+}
+
+//===- Misc. Helpers -----------------------------------------------------===//
+
+/// Copies a StringRef into a static pool to preserve it.
+/// Most Pattern classes use StringRef so we need this.
+StringRef insertStrRef(StringRef S) {
+ if (S.empty())
+ return {};
+
+ static StringSet<> Pool;
+ auto [It, Inserted] = Pool.insert(S);
+ return It->getKey();
+}
+
+template <typename Container> auto keys(Container &&C) {
+ return map_range(C, [](auto &Entry) -> auto & { return Entry.first; });
+}
+
+template <typename Container> auto values(Container &&C) {
+ return map_range(C, [](auto &Entry) -> auto & { return Entry.second; });
+}
+
+std::string getIsEnabledPredicateEnumName(unsigned CombinerRuleID) {
+ return "GICXXPred_Simple_IsRule" + to_string(CombinerRuleID) + "Enabled";
+}
+
+//===- MatchTable Helpers ------------------------------------------------===//
+
+LLTCodeGen getLLTCodeGen(const PatternType &PT) {
+ return *MVTToLLT(getValueType(PT.getLLTRecord()));
+}
+
+LLTCodeGenOrTempType getLLTCodeGenOrTempType(const PatternType &PT,
+ RuleMatcher &RM) {
+ assert(!PT.isNone());
+
+ if (PT.isLLT())
+ return getLLTCodeGen(PT);
+
+ assert(PT.isTypeOf());
+ auto &OM = RM.getOperandMatcher(PT.getTypeOfOpName());
+ return OM.getTempTypeIdx(RM);
+}
+
+//===- PrettyStackTrace Helpers ------------------------------------------===//
+
+class PrettyStackTraceParse : public PrettyStackTraceEntry {
+ const Record &Def;
+
+public:
+ PrettyStackTraceParse(const Record &Def) : Def(Def) {}
+
+ void print(raw_ostream &OS) const override {
+ if (Def.isSubClassOf("GICombineRule"))
+ OS << "Parsing GICombineRule '" << Def.getName() << "'";
+ else if (Def.isSubClassOf(PatFrag::ClassName))
+ OS << "Parsing " << PatFrag::ClassName << " '" << Def.getName() << "'";
+ else
+ OS << "Parsing '" << Def.getName() << "'";
+ OS << '\n';
+ }
+};
+
+class PrettyStackTraceEmit : public PrettyStackTraceEntry {
+ const Record &Def;
+ const Pattern *Pat = nullptr;
+
+public:
+ PrettyStackTraceEmit(const Record &Def, const Pattern *Pat = nullptr)
+ : Def(Def), Pat(Pat) {}
+
+ void print(raw_ostream &OS) const override {
+ if (Def.isSubClassOf("GICombineRule"))
+ OS << "Emitting GICombineRule '" << Def.getName() << "'";
+ else if (Def.isSubClassOf(PatFrag::ClassName))
+ OS << "Emitting " << PatFrag::ClassName << " '" << Def.getName() << "'";
+ else
+ OS << "Emitting '" << Def.getName() << "'";
+
+ if (Pat)
+ OS << " [" << Pat->getKindName() << " '" << Pat->getName() << "']";
+ OS << '\n';
+ }
+};
+
+//===- CombineRuleOperandTypeChecker --------------------------------------===//
+
+/// This is a wrapper around OperandTypeChecker specialized for Combiner Rules.
+/// On top of doing the same things as OperandTypeChecker, this also attempts to
+/// infer as many types as possible for temporary register defs & immediates in
+/// apply patterns.
+///
+/// The inference is trivial and leverages the MCOI OperandTypes encoded in
+/// CodeGenInstructions to infer types across patterns in a CombineRule. It's
+/// thus very limited and only supports CodeGenInstructions (but that's the main
+/// use case so it's fine).
+///
+/// We only try to infer untyped operands in apply patterns when they're temp
+/// reg defs, or immediates. Inference always outputs a `TypeOf<$x>` where $x is
+/// a named operand from a match pattern.
+class CombineRuleOperandTypeChecker : private OperandTypeChecker {
+public:
+ CombineRuleOperandTypeChecker(const Record &RuleDef,
+ const OperandTable &MatchOpTable)
+ : OperandTypeChecker(RuleDef.getLoc()), RuleDef(RuleDef),
+ MatchOpTable(MatchOpTable) {}
+
+ /// Records and checks a 'match' pattern.
+ bool processMatchPattern(InstructionPattern &P);
+
+ /// Records and checks an 'apply' pattern.
+ bool processApplyPattern(InstructionPattern &P);
+
+ /// Propagates types, then perform type inference and do a second round of
+ /// propagation in the apply patterns only if any types were inferred.
+ void propagateAndInferTypes();
+
+private:
+ /// TypeEquivalenceClasses are groups of operands of an instruction that share
+ /// a common type.
+ ///
+ /// e.g. [[a, b], [c, d]] means a and b have the same type, and c and
+ /// d have the same type too. b/c and a/d don't have to have the same type,
+ /// though.
+ using TypeEquivalenceClasses = EquivalenceClasses<StringRef>;
+
+ /// \returns true for `OPERAND_GENERIC_` 0 through 5.
+ /// These are the MCOI types that can be registers. The other MCOI types are
+ /// either immediates, or fancier operands used only post-ISel, so we don't
+ /// care about them for combiners.
+ static bool canMCOIOperandTypeBeARegister(StringRef MCOIType) {
+ // Assume OPERAND_GENERIC_0 through 5 can be registers. The other MCOI
+ // OperandTypes are either never used in gMIR, or not relevant (e.g.
+ // OPERAND_GENERIC_IMM, which is definitely never a register).
+ return MCOIType.drop_back(1).ends_with("OPERAND_GENERIC_");
+ }
+
+ /// Finds the "MCOI::"" operand types for each operand of \p CGP.
+ ///
+ /// This is a bit trickier than it looks because we need to handle variadic
+ /// in/outs.
+ ///
+ /// e.g. for
+ /// (G_BUILD_VECTOR $vec, $x, $y) ->
+ /// [MCOI::OPERAND_GENERIC_0, MCOI::OPERAND_GENERIC_1,
+ /// MCOI::OPERAND_GENERIC_1]
+ ///
+ /// For unknown types (which can happen in variadics where varargs types are
+ /// inconsistent), a unique name is given, e.g. "unknown_type_0".
+ static std::vector<std::string>
+ getMCOIOperandTypes(const CodeGenInstructionPattern &CGP);
+
+ /// Adds the TypeEquivalenceClasses for \p P in \p OutTECs.
+ void getInstEqClasses(const InstructionPattern &P,
+ TypeEquivalenceClasses &OutTECs) const;
+
+ /// Calls `getInstEqClasses` on all patterns of the rule to produce the whole
+ /// rule's TypeEquivalenceClasses.
+ TypeEquivalenceClasses getRuleEqClasses() const;
+
+ /// Tries to infer the type of the \p ImmOpIdx -th operand of \p IP using \p
+ /// TECs.
+ ///
+ /// This is achieved by trying to find a named operand in \p IP that shares
+ /// the same type as \p ImmOpIdx, and using \ref inferNamedOperandType on that
+ /// operand instead.
+ ///
+ /// \returns the inferred type or an empty PatternType if inference didn't
+ /// succeed.
+ PatternType inferImmediateType(const InstructionPattern &IP,
+ unsigned ImmOpIdx,
+ const TypeEquivalenceClasses &TECs) const;
+
+ /// Looks inside \p TECs to infer \p OpName's type.
+ ///
+ /// \returns the inferred type or an empty PatternType if inference didn't
+ /// succeed.
+ PatternType inferNamedOperandType(const InstructionPattern &IP,
+ StringRef OpName,
+ const TypeEquivalenceClasses &TECs) const;
+
+ const Record &RuleDef;
+ SmallVector<InstructionPattern *, 8> MatchPats;
+ SmallVector<InstructionPattern *, 8> ApplyPats;
+
+ const OperandTable &MatchOpTable;
+};
+
+bool CombineRuleOperandTypeChecker::processMatchPattern(InstructionPattern &P) {
+ MatchPats.push_back(&P);
+ return check(P, /*CheckTypeOf*/ [](const auto &) {
+ // GITypeOf in 'match' is currently always rejected by the
+ // CombineRuleBuilder after inference is done.
+ return true;
+ });
+}
+
+bool CombineRuleOperandTypeChecker::processApplyPattern(InstructionPattern &P) {
+ ApplyPats.push_back(&P);
+ return check(P, /*CheckTypeOf*/ [&](const PatternType &Ty) {
+ // GITypeOf<"$x"> can only be used if "$x" is a matched operand.
+ const auto OpName = Ty.getTypeOfOpName();
+ if (MatchOpTable.lookup(OpName).Found)
+ return true;
+
+ PrintError(RuleDef.getLoc(), "'" + OpName + "' ('" + Ty.str() +
+ "') does not refer to a matched operand!");
+ return false;
+ });
+}
+
+void CombineRuleOperandTypeChecker::propagateAndInferTypes() {
+ /// First step here is to propagate types using the OperandTypeChecker. That
+ /// way we ensure all uses of a given register have consistent types.
+ propagateTypes();
+
+ /// Build the TypeEquivalenceClasses for the whole rule.
+ const TypeEquivalenceClasses TECs = getRuleEqClasses();
+
+ /// Look at the apply patterns and find operands that need to be
+ /// inferred. We then try to find an equivalence class that they're a part of
+ /// and select the best operand to use for the `GITypeOf` type. We prioritize
+ /// defs of matched instructions because those are guaranteed to be registers.
+ bool InferredAny = false;
+ for (auto *Pat : ApplyPats) {
+ for (unsigned K = 0; K < Pat->operands_size(); ++K) {
+ auto &Op = Pat->getOperand(K);
+
+ // We only want to take a look at untyped defs or immediates.
+ if ((!Op.isDef() && !Op.hasImmValue()) || Op.getType())
+ continue;
+
+ // Infer defs & named immediates.
+ if (Op.isDef() || Op.isNamedImmediate()) {
+ // Check it's not a redefinition of a matched operand.
+ // In such cases, inference is not necessary because we just copy
+ // operands and don't create temporary registers.
+ if (MatchOpTable.lookup(Op.getOperandName()).Found)
+ continue;
+
+ // Inference is needed here, so try to do it.
+ if (PatternType Ty =
+ inferNamedOperandType(*Pat, Op.getOperandName(), TECs)) {
+ if (DebugTypeInfer)
+ errs() << "INFER: " << Op.describe() << " -> " << Ty.str() << '\n';
+ Op.setType(Ty);
+ InferredAny = true;
+ }
+
+ continue;
+ }
+
+ // Infer immediates
+ if (Op.hasImmValue()) {
+ if (PatternType Ty = inferImmediateType(*Pat, K, TECs)) {
+ if (DebugTypeInfer)
+ errs() << "INFER: " << Op.describe() << " -> " << Ty.str() << '\n';
+ Op.setType(Ty);
+ InferredAny = true;
+ }
+ continue;
+ }
+ }
+ }
+
+ // If we've inferred any types, we want to propagate them across the apply
+ // patterns. Type inference only adds GITypeOf types that point to Matched
+ // operands, so we definitely don't want to propagate types into the match
+ // patterns as well, otherwise bad things happen.
+ if (InferredAny) {
+ OperandTypeChecker OTC(RuleDef.getLoc());
+ for (auto *Pat : ApplyPats) {
+ if (!OTC.check(*Pat, [&](const auto &) { return true; }))
+ PrintFatalError(RuleDef.getLoc(),
+ "OperandTypeChecker unexpectedly failed on '" +
+ Pat->getName() + "' during Type Inference");
+ }
+ OTC.propagateTypes();
+
+ if (DebugTypeInfer) {
+ errs() << "Apply patterns for rule " << RuleDef.getName()
+ << " after inference:\n";
+ for (auto *Pat : ApplyPats) {
+ errs() << " ";
+ Pat->print(errs(), /*PrintName*/ true);
+ errs() << '\n';
+ }
+ errs() << '\n';
+ }
+ }
+}
+
+PatternType CombineRuleOperandTypeChecker::inferImmediateType(
+ const InstructionPattern &IP, unsigned ImmOpIdx,
+ const TypeEquivalenceClasses &TECs) const {
+ // We can only infer CGPs.
+ const auto *CGP = dyn_cast<CodeGenInstructionPattern>(&IP);
+ if (!CGP)
+ return {};
+
+ // For CGPs, we try to infer immediates by trying to infer another named
+ // operand that shares its type.
+ //
+ // e.g.
+ // Pattern: G_BUILD_VECTOR $x, $y, 0
+ // MCOIs: [MCOI::OPERAND_GENERIC_0, MCOI::OPERAND_GENERIC_1,
+ // MCOI::OPERAND_GENERIC_1]
+ // $y has the same type as 0, so we can infer $y and get the type 0 should
+ // have.
+
+ // We infer immediates by looking for a named operand that shares the same
+ // MCOI type.
+ const auto MCOITypes = getMCOIOperandTypes(*CGP);
+ StringRef ImmOpTy = MCOITypes[ImmOpIdx];
+
+ for (const auto &[Idx, Ty] : enumerate(MCOITypes)) {
+ if (Idx != ImmOpIdx && Ty == ImmOpTy) {
+ const auto &Op = IP.getOperand(Idx);
+ if (!Op.isNamedOperand())
+ continue;
+
+ // Named operand with the same name, try to infer that.
+ if (PatternType InferTy =
+ inferNamedOperandType(IP, Op.getOperandName(), TECs))
+ return InferTy;
+ }
+ }
+
+ return {};
+}
+
+PatternType CombineRuleOperandTypeChecker::inferNamedOperandType(
+ const InstructionPattern &IP, StringRef OpName,
+ const TypeEquivalenceClasses &TECs) const {
+ // This is the simplest possible case, we just need to find a TEC that
+ // contains OpName. Look at all other operands in equivalence class and try to
+ // find a suitable one.
+
+ // Check for a def of a matched pattern. This is guaranteed to always
+ // be a register so we can blindly use that.
+ StringRef GoodOpName;
+ for (auto It = TECs.findLeader(OpName); It != TECs.member_end(); ++It) {
+ if (*It == OpName)
+ continue;
+
+ const auto LookupRes = MatchOpTable.lookup(*It);
+ if (LookupRes.Def) // Favor defs
+ return PatternType::getTypeOf(*It);
+
+ // Otherwise just save this in case we don't find any def.
+ if (GoodOpName.empty() && LookupRes.Found)
+ GoodOpName = *It;
+ }
+
+ if (!GoodOpName.empty())
+ return PatternType::getTypeOf(GoodOpName);
+
+ // No good operand found, give up.
+ return {};
+}
+
+std::vector<std::string> CombineRuleOperandTypeChecker::getMCOIOperandTypes(
+ const CodeGenInstructionPattern &CGP) {
+ // FIXME?: Should we cache this? We call it twice when inferring immediates.
+
+ static unsigned UnknownTypeIdx = 0;
+
+ std::vector<std::string> OpTypes;
+ auto &CGI = CGP.getInst();
+ Record *VarArgsTy = CGI.TheDef->isSubClassOf("GenericInstruction")
+ ? CGI.TheDef->getValueAsOptionalDef("variadicOpsType")
+ : nullptr;
+ std::string VarArgsTyName =
+ VarArgsTy ? ("MCOI::" + VarArgsTy->getValueAsString("OperandType")).str()
+ : ("unknown_type_" + Twine(UnknownTypeIdx++)).str();
+
+ // First, handle defs.
+ for (unsigned K = 0; K < CGI.Operands.NumDefs; ++K)
+ OpTypes.push_back(CGI.Operands[K].OperandType);
+
+ // Then, handle variadic defs if there are any.
+ if (CGP.hasVariadicDefs()) {
+ for (unsigned K = CGI.Operands.NumDefs; K < CGP.getNumInstDefs(); ++K)
+ OpTypes.push_back(VarArgsTyName);
+ }
+
+ // If we had variadic defs, the op idx in the pattern won't match the op idx
+ // in the CGI anymore.
+ int CGIOpOffset = int(CGI.Operands.NumDefs) - CGP.getNumInstDefs();
+ assert(CGP.hasVariadicDefs() ? (CGIOpOffset <= 0) : (CGIOpOffset == 0));
+
+ // Handle all remaining use operands, including variadic ones.
+ for (unsigned K = CGP.getNumInstDefs(); K < CGP.getNumInstOperands(); ++K) {
+ unsigned CGIOpIdx = K + CGIOpOffset;
+ if (CGIOpIdx >= CGI.Operands.size()) {
+ assert(CGP.isVariadic());
+ OpTypes.push_back(VarArgsTyName);
+ } else {
+ OpTypes.push_back(CGI.Operands[CGIOpIdx].OperandType);
+ }
+ }
+
+ assert(OpTypes.size() == CGP.operands_size());
+ return OpTypes;
+}
+
+void CombineRuleOperandTypeChecker::getInstEqClasses(
+ const InstructionPattern &P, TypeEquivalenceClasses &OutTECs) const {
+ // Determine the TypeEquivalenceClasses by:
+ // - Getting the MCOI Operand Types.
+ // - Creating a Map of MCOI Type -> [Operand Indexes]
+ // - Iterating over the map, filtering types we don't like, and just adding
+ // the array of Operand Indexes to \p OutTECs.
+
+ // We can only do this on CodeGenInstructions. Other InstructionPatterns have
+ // no type inference information associated with them.
+ // TODO: Could we add some inference information to builtins at least? e.g.
+ // ReplaceReg should always replace with a reg of the same type, for instance.
+ // Though, those patterns are often used alone so it might not be worth the
+ // trouble to infer their types.
+ auto *CGP = dyn_cast<CodeGenInstructionPattern>(&P);
+ if (!CGP)
+ return;
+
+ const auto MCOITypes = getMCOIOperandTypes(*CGP);
+ assert(MCOITypes.size() == P.operands_size());
+
+ DenseMap<StringRef, std::vector<unsigned>> TyToOpIdx;
+ for (const auto &[Idx, Ty] : enumerate(MCOITypes))
+ TyToOpIdx[Ty].push_back(Idx);
+
+ if (DebugTypeInfer)
+ errs() << "\tGroups for " << P.getName() << ":\t";
+
+ for (const auto &[Ty, Idxs] : TyToOpIdx) {
+ if (!canMCOIOperandTypeBeARegister(Ty))
+ continue;
+
+ if (DebugTypeInfer)
+ errs() << '[';
+ StringRef Sep = "";
+
+ // We only collect named operands.
+ StringRef Leader;
+ for (unsigned Idx : Idxs) {
+ const auto &Op = P.getOperand(Idx);
+ if (!Op.isNamedOperand())
+ continue;
+
+ const auto OpName = Op.getOperandName();
+ if (DebugTypeInfer) {
+ errs() << Sep << OpName;
+ Sep = ", ";
+ }
+
+ if (Leader.empty())
+ OutTECs.insert((Leader = OpName));
+ else
+ OutTECs.unionSets(Leader, OpName);
+ }
+
+ if (DebugTypeInfer)
+ errs() << "] ";
+ }
+
+ if (DebugTypeInfer)
+ errs() << '\n';
+}
+
+CombineRuleOperandTypeChecker::TypeEquivalenceClasses
+CombineRuleOperandTypeChecker::getRuleEqClasses() const {
+ StringMap<unsigned> OpNameToEqClassIdx;
+ TypeEquivalenceClasses TECs;
+
+ if (DebugTypeInfer)
+ errs() << "Rule Operand Type Equivalence Classes for " << RuleDef.getName()
+ << ":\n";
+
+ for (const auto *Pat : MatchPats)
+ getInstEqClasses(*Pat, TECs);
+ for (const auto *Pat : ApplyPats)
+ getInstEqClasses(*Pat, TECs);
+
+ if (DebugTypeInfer) {
+ errs() << "Final Type Equivalence Classes: ";
+ for (auto ClassIt = TECs.begin(); ClassIt != TECs.end(); ++ClassIt) {
+ // only print non-empty classes.
+ if (auto MembIt = TECs.member_begin(ClassIt);
+ MembIt != TECs.member_end()) {
+ errs() << '[';
+ StringRef Sep = "";
+ for (; MembIt != TECs.member_end(); ++MembIt) {
+ errs() << Sep << *MembIt;
+ Sep = ", ";
+ }
+ errs() << "] ";
+ }
+ }
+ errs() << '\n';
+ }
+
+ return TECs;
+}
+
+//===- CombineRuleBuilder -------------------------------------------------===//
+
+/// 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<StringRef, std::unique_ptr<Pattern>>;
+ using PatternAlternatives = DenseMap<const Pattern *, unsigned>;
+
+ CombineRuleBuilder(const CodeGenTarget &CGT,
+ SubtargetFeatureInfoMap &SubtargetFeatures,
+ Record &RuleDef, unsigned ID,
+ std::vector<RuleMatcher> &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.
+#ifndef NDEBUG
+ void verify() const;
+#endif
+
+private:
+ const CodeGenInstruction &getGConstant() const {
+ return CGT.getInstruction(RuleDef.getRecords().getDef("G_CONSTANT"));
+ }
+
+ void PrintError(Twine Msg) const { ::PrintError(&RuleDef, Msg); }
+ void PrintWarning(Twine Msg) const { ::PrintWarning(RuleDef.getLoc(), Msg); }
+ void PrintNote(Twine Msg) const { ::PrintNote(RuleDef.getLoc(), Msg); }
+
+ void print(raw_ostream &OS, const PatternAlternatives &Alts) const;
+
+ bool addApplyPattern(std::unique_ptr<Pattern> Pat);
+ bool addMatchPattern(std::unique_ptr<Pattern> Pat);
+
+ /// Adds the expansions from \see MatchDatas to \p CE.
+ void declareAllMatchDatasExpansions(CodeExpansions &CE) const;
+
+ /// Adds a matcher \p P to \p IM, expanding its code using \p CE.
+ /// Note that the predicate is added on the last InstructionMatcher.
+ ///
+ /// \p Alts is only used if DebugCXXPreds is enabled.
+ void addCXXPredicate(RuleMatcher &M, const CodeExpansions &CE,
+ const CXXPattern &P, const PatternAlternatives &Alts);
+
+ /// Adds an apply \p P to \p IM, expanding its code using \p CE.
+ void addCXXAction(RuleMatcher &M, const CodeExpansions &CE,
+ const CXXPattern &P);
+
+ bool hasOnlyCXXApplyPatterns() const;
+ bool hasEraseRoot() const;
+
+ // Infer machine operand types and check their consistency.
+ bool typecheckPatterns();
+
+ /// For all PatFragPatterns, add a new entry in PatternAlternatives for each
+ /// PatternList it contains. This is multiplicative, so if we have 2
+ /// PatFrags with 3 alternatives each, we get 2*3 permutations added to
+ /// PermutationsToEmit. The "MaxPermutations" field controls how many
+ /// permutations are allowed before an error is emitted and this function
+ /// returns false. This is a simple safeguard to prevent combination of
+ /// PatFrags from generating enormous amounts of rules.
+ bool buildPermutationsToEmit();
+
+ /// Checks additional semantics of the Patterns.
+ bool checkSemantics();
+
+ /// Creates a new RuleMatcher with some boilerplate
+ /// settings/actions/predicates, and and adds it to \p OutRMs.
+ /// \see addFeaturePredicates too.
+ ///
+ /// \param Alts Current set of alternatives, for debug comment.
+ /// \param AdditionalComment Comment string to be added to the
+ /// `DebugCommentAction`.
+ RuleMatcher &addRuleMatcher(const PatternAlternatives &Alts,
+ Twine AdditionalComment = "");
+ bool addFeaturePredicates(RuleMatcher &M);
+
+ bool findRoots();
+ bool buildRuleOperandsTable();
+
+ bool parseDefs(const DagInit &Def);
+ bool
+ parsePatternList(const DagInit &List,
+ function_ref<bool(std::unique_ptr<Pattern>)> ParseAction,
+ StringRef Operator, ArrayRef<SMLoc> DiagLoc,
+ StringRef AnonPatNamePrefix) const;
+
+ std::unique_ptr<Pattern> parseInstructionPattern(const Init &Arg,
+ StringRef PatName) const;
+ std::unique_ptr<Pattern> parseWipMatchOpcodeMatcher(const Init &Arg,
+ StringRef PatName) const;
+ bool parseInstructionPatternOperand(InstructionPattern &IP,
+ const Init *OpInit,
+ const StringInit *OpName) const;
+ bool parseInstructionPatternMIFlags(InstructionPattern &IP,
+ const DagInit *Op) const;
+ std::unique_ptr<PatFrag> parsePatFragImpl(const Record *Def) const;
+ bool parsePatFragParamList(
+ ArrayRef<SMLoc> DiagLoc, const DagInit &OpsList,
+ function_ref<bool(StringRef, PatFrag::ParamKind)> ParseAction) const;
+ const PatFrag *parsePatFrag(const Record *Def) const;
+
+ bool emitMatchPattern(CodeExpansions &CE, const PatternAlternatives &Alts,
+ const InstructionPattern &IP);
+ bool emitMatchPattern(CodeExpansions &CE, const PatternAlternatives &Alts,
+ const AnyOpcodePattern &AOP);
+
+ bool emitPatFragMatchPattern(CodeExpansions &CE,
+ const PatternAlternatives &Alts, RuleMatcher &RM,
+ InstructionMatcher *IM,
+ const PatFragPattern &PFP,
+ DenseSet<const Pattern *> &SeenPats);
+
+ bool emitApplyPatterns(CodeExpansions &CE, RuleMatcher &M);
+
+ // Recursively visits InstructionPatterns from P to build up the
+ // RuleMatcher actions.
+ bool emitInstructionApplyPattern(CodeExpansions &CE, RuleMatcher &M,
+ const InstructionPattern &P,
+ DenseSet<const Pattern *> &SeenPats,
+ StringMap<unsigned> &OperandToTempRegID);
+
+ bool emitCodeGenInstructionApplyImmOperand(RuleMatcher &M,
+ BuildMIAction &DstMI,
+ const CodeGenInstructionPattern &P,
+ const InstructionOperand &O);
+
+ bool emitBuiltinApplyPattern(CodeExpansions &CE, RuleMatcher &M,
+ const BuiltinPattern &P,
+ StringMap<unsigned> &OperandToTempRegID);
+
+ // Recursively visits CodeGenInstructionPattern from P to build up the
+ // RuleMatcher/InstructionMatcher. May create new InstructionMatchers as
+ // needed.
+ using OperandMapperFnRef =
+ function_ref<InstructionOperand(const InstructionOperand &)>;
+ using OperandDefLookupFn =
+ function_ref<const InstructionPattern *(StringRef)>;
+ bool emitCodeGenInstructionMatchPattern(
+ CodeExpansions &CE, const PatternAlternatives &Alts, RuleMatcher &M,
+ InstructionMatcher &IM, const CodeGenInstructionPattern &P,
+ DenseSet<const Pattern *> &SeenPats, OperandDefLookupFn LookupOperandDef,
+ OperandMapperFnRef OperandMapper = [](const auto &O) { return O; });
+
+ const CodeGenTarget &CGT;
+ SubtargetFeatureInfoMap &SubtargetFeatures;
+ Record &RuleDef;
+ const unsigned RuleID;
+ std::vector<RuleMatcher> &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;
+
+ /// Operand tables to tie match/apply patterns together.
+ OperandTable MatchOpTable;
+ OperandTable ApplyOpTable;
+
+ /// Set by findRoots.
+ Pattern *MatchRoot = nullptr;
+ SmallDenseSet<InstructionPattern *, 2> ApplyRoots;
+
+ SmallVector<MatchDataInfo, 2> MatchDatas;
+ SmallVector<PatternAlternatives, 1> PermutationsToEmit;
+
+ // print()/debug-only members.
+ mutable SmallPtrSet<const PatFrag *, 2> SeenPatFrags;
+};
+
+bool CombineRuleBuilder::parseAll() {
+ auto StackTrace = PrettyStackTraceParse(RuleDef);
+
+ if (!parseDefs(*RuleDef.getValueAsDag("Defs")))
+ return false;
+
+ if (!parsePatternList(
+ *RuleDef.getValueAsDag("Match"),
+ [this](auto Pat) { return addMatchPattern(std::move(Pat)); }, "match",
+ RuleDef.getLoc(), (RuleDef.getName() + "_match").str()))
+ return false;
+
+ if (!parsePatternList(
+ *RuleDef.getValueAsDag("Apply"),
+ [this](auto Pat) { return addApplyPattern(std::move(Pat)); }, "apply",
+ RuleDef.getLoc(), (RuleDef.getName() + "_apply").str()))
+ return false;
+
+ if (!buildRuleOperandsTable() || !typecheckPatterns() || !findRoots() ||
+ !checkSemantics() || !buildPermutationsToEmit())
+ return false;
+ LLVM_DEBUG(verify());
+ return true;
+}
+
+bool CombineRuleBuilder::emitRuleMatchers() {
+ auto StackTrace = PrettyStackTraceEmit(RuleDef);
+
+ assert(MatchRoot);
+ CodeExpansions CE;
+ declareAllMatchDatasExpansions(CE);
+
+ assert(!PermutationsToEmit.empty());
+ for (const auto &Alts : PermutationsToEmit) {
+ switch (MatchRoot->getKind()) {
+ case Pattern::K_AnyOpcode: {
+ if (!emitMatchPattern(CE, Alts, *cast<AnyOpcodePattern>(MatchRoot)))
+ return false;
+ break;
+ }
+ case Pattern::K_PatFrag:
+ case Pattern::K_Builtin:
+ case Pattern::K_CodeGenInstruction:
+ if (!emitMatchPattern(CE, Alts, *cast<InstructionPattern>(MatchRoot)))
+ return false;
+ break;
+ case Pattern::K_CXX:
+ PrintError("C++ code cannot be the root of a rule!");
+ 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';
+
+ if (!MatchDatas.empty()) {
+ OS << " (MatchDatas\n";
+ for (const auto &MD : MatchDatas) {
+ OS << " ";
+ MD.print(OS);
+ OS << '\n';
+ }
+ OS << " )\n";
+ }
+
+ if (!SeenPatFrags.empty()) {
+ OS << " (PatFrags\n";
+ for (const auto *PF : SeenPatFrags) {
+ PF->print(OS, /*Indent=*/" ");
+ OS << '\n';
+ }
+ OS << " )\n";
+ }
+
+ const auto DumpPats = [&](StringRef Name, const PatternMap &Pats) {
+ OS << " (" << Name << " ";
+ if (Pats.empty()) {
+ OS << "<empty>)\n";
+ return;
+ }
+
+ OS << '\n';
+ for (const auto &[Name, Pat] : Pats) {
+ OS << " ";
+ if (Pat.get() == MatchRoot)
+ OS << "<match_root>";
+ if (isa<InstructionPattern>(Pat.get()) &&
+ ApplyRoots.contains(cast<InstructionPattern>(Pat.get())))
+ OS << "<apply_root>";
+ OS << Name << ":";
+ Pat->print(OS, /*PrintName=*/false);
+ OS << '\n';
+ }
+ OS << " )\n";
+ };
+
+ DumpPats("MatchPats", MatchPats);
+ DumpPats("ApplyPats", ApplyPats);
+
+ MatchOpTable.print(OS, "MatchPats", /*Indent*/ " ");
+ ApplyOpTable.print(OS, "ApplyPats", /*Indent*/ " ");
+
+ if (PermutationsToEmit.size() > 1) {
+ OS << " (PermutationsToEmit\n";
+ for (const auto &Perm : PermutationsToEmit) {
+ OS << " ";
+ print(OS, Perm);
+ OS << ",\n";
+ }
+ OS << " )\n";
+ }
+
+ OS << ")\n";
+}
+
+#ifndef NDEBUG
+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());
+ }
+
+ // Sanity check: the map should point to the same data as the Pattern.
+ // Both strings are allocated in the pool using insertStrRef.
+ 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);
+
+ // Check there are no wip_match_opcode patterns in the "apply" patterns.
+ if (any_of(ApplyPats,
+ [&](auto &E) { return isa<AnyOpcodePattern>(E.second.get()); })) {
+ dump();
+ PrintFatalError(
+ "illegal wip_match_opcode pattern in the 'apply' patterns!");
+ }
+
+ // Check there are no nullptrs in ApplyRoots.
+ if (ApplyRoots.contains(nullptr)) {
+ PrintFatalError(
+ "CombineRuleBuilder's ApplyRoots set contains a null pointer!");
+ }
+}
+#endif
+
+void CombineRuleBuilder::print(raw_ostream &OS,
+ const PatternAlternatives &Alts) const {
+ SmallVector<std::string, 1> Strings(
+ map_range(Alts, [](const auto &PatAndPerm) {
+ return PatAndPerm.first->getName().str() + "[" +
+ to_string(PatAndPerm.second) + "]";
+ }));
+ // Sort so output is deterministic for tests. Otherwise it's sorted by pointer
+ // values.
+ sort(Strings);
+ OS << "[" << join(Strings, ", ") << "]";
+}
+
+bool CombineRuleBuilder::addApplyPattern(std::unique_ptr<Pattern> Pat) {
+ StringRef Name = Pat->getName();
+ if (ApplyPats.contains(Name)) {
+ PrintError("'" + Name + "' apply pattern defined more than once!");
+ return false;
+ }
+
+ if (isa<AnyOpcodePattern>(Pat.get())) {
+ PrintError("'" + Name +
+ "': wip_match_opcode is not supported in apply patterns");
+ return false;
+ }
+
+ if (isa<PatFragPattern>(Pat.get())) {
+ PrintError("'" + Name + "': using " + PatFrag::ClassName +
+ " is not supported in apply patterns");
+ return false;
+ }
+
+ if (auto *CXXPat = dyn_cast<CXXPattern>(Pat.get()))
+ CXXPat->setIsApply();
+
+ ApplyPats[Name] = std::move(Pat);
+ return true;
+}
+
+bool CombineRuleBuilder::addMatchPattern(std::unique_ptr<Pattern> Pat) {
+ StringRef Name = Pat->getName();
+ if (MatchPats.contains(Name)) {
+ PrintError("'" + Name + "' match pattern defined more than once!");
+ return false;
+ }
+
+ // For now, none of the builtins can appear in 'match'.
+ if (const auto *BP = dyn_cast<BuiltinPattern>(Pat.get())) {
+ PrintError("'" + BP->getInstName() +
+ "' cannot be used in a 'match' pattern");
+ return false;
+ }
+
+ MatchPats[Name] = std::move(Pat);
+ return true;
+}
+
+void CombineRuleBuilder::declareAllMatchDatasExpansions(
+ CodeExpansions &CE) const {
+ for (const auto &MD : MatchDatas)
+ CE.declare(MD.getPatternSymbol(), MD.getQualifiedVariableName());
+}
+
+void CombineRuleBuilder::addCXXPredicate(RuleMatcher &M,
+ const CodeExpansions &CE,
+ const CXXPattern &P,
+ const PatternAlternatives &Alts) {
+ // FIXME: Hack so C++ code is executed last. May not work for more complex
+ // patterns.
+ auto &IM = *std::prev(M.insnmatchers().end());
+ auto Loc = RuleDef.getLoc();
+ const auto AddComment = [&](raw_ostream &OS) {
+ OS << "// Pattern Alternatives: ";
+ print(OS, Alts);
+ OS << '\n';
+ };
+ const auto &ExpandedCode =
+ DebugCXXPreds ? P.expandCode(CE, Loc, AddComment) : P.expandCode(CE, Loc);
+ IM->addPredicate<GenericInstructionPredicateMatcher>(
+ ExpandedCode.getEnumNameWithPrefix(CXXPredPrefix));
+}
+
+void CombineRuleBuilder::addCXXAction(RuleMatcher &M, const CodeExpansions &CE,
+ const CXXPattern &P) {
+ const auto &ExpandedCode = P.expandCode(CE, RuleDef.getLoc());
+ M.addAction<CustomCXXAction>(
+ ExpandedCode.getEnumNameWithPrefix(CXXApplyPrefix));
+}
+
+bool CombineRuleBuilder::hasOnlyCXXApplyPatterns() const {
+ return all_of(ApplyPats, [&](auto &Entry) {
+ return isa<CXXPattern>(Entry.second.get());
+ });
+}
+
+bool CombineRuleBuilder::hasEraseRoot() const {
+ return any_of(ApplyPats, [&](auto &Entry) {
+ if (const auto *BP = dyn_cast<BuiltinPattern>(Entry.second.get()))
+ return BP->getBuiltinKind() == BI_EraseRoot;
+ return false;
+ });
+}
+
+bool CombineRuleBuilder::typecheckPatterns() {
+ CombineRuleOperandTypeChecker OTC(RuleDef, MatchOpTable);
+
+ for (auto &Pat : values(MatchPats)) {
+ if (auto *IP = dyn_cast<InstructionPattern>(Pat.get())) {
+ if (!OTC.processMatchPattern(*IP))
+ return false;
+ }
+ }
+
+ for (auto &Pat : values(ApplyPats)) {
+ if (auto *IP = dyn_cast<InstructionPattern>(Pat.get())) {
+ if (!OTC.processApplyPattern(*IP))
+ return false;
+ }
+ }
+
+ OTC.propagateAndInferTypes();
+
+ // Always check this after in case inference adds some special types to the
+ // match patterns.
+ for (auto &Pat : values(MatchPats)) {
+ if (auto *IP = dyn_cast<InstructionPattern>(Pat.get())) {
+ if (IP->diagnoseAllSpecialTypes(
+ RuleDef.getLoc(), PatternType::SpecialTyClassName +
+ " is not supported in 'match' patterns")) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool CombineRuleBuilder::buildPermutationsToEmit() {
+ PermutationsToEmit.clear();
+
+ // Start with one empty set of alternatives.
+ PermutationsToEmit.emplace_back();
+ for (const auto &Pat : values(MatchPats)) {
+ unsigned NumAlts = 0;
+ // Note: technically, AnyOpcodePattern also needs permutations, but:
+ // - We only allow a single one of them in the root.
+ // - They cannot be mixed with any other pattern other than C++ code.
+ // So we don't really need to take them into account here. We could, but
+ // that pattern is a hack anyway and the less it's involved, the better.
+ if (const auto *PFP = dyn_cast<PatFragPattern>(Pat.get()))
+ NumAlts = PFP->getPatFrag().num_alternatives();
+ else
+ continue;
+
+ // For each pattern that needs permutations, multiply the current set of
+ // alternatives.
+ auto CurPerms = PermutationsToEmit;
+ PermutationsToEmit.clear();
+
+ for (const auto &Perm : CurPerms) {
+ assert(!Perm.count(Pat.get()) && "Pattern already emitted?");
+ for (unsigned K = 0; K < NumAlts; ++K) {
+ PatternAlternatives NewPerm = Perm;
+ NewPerm[Pat.get()] = K;
+ PermutationsToEmit.emplace_back(std::move(NewPerm));
+ }
+ }
+ }
+
+ if (int64_t MaxPerms = RuleDef.getValueAsInt("MaxPermutations");
+ MaxPerms > 0) {
+ if ((int64_t)PermutationsToEmit.size() > MaxPerms) {
+ PrintError("cannot emit rule '" + RuleDef.getName() + "'; " +
+ Twine(PermutationsToEmit.size()) +
+ " permutations would be emitted, but the max is " +
+ Twine(MaxPerms));
+ return false;
+ }
+ }
+
+ // Ensure we always have a single empty entry, it simplifies the emission
+ // logic so it doesn't need to handle the case where there are no perms.
+ if (PermutationsToEmit.empty()) {
+ PermutationsToEmit.emplace_back();
+ return true;
+ }
+
+ return true;
+}
+
+bool CombineRuleBuilder::checkSemantics() {
+ assert(MatchRoot && "Cannot call this before findRoots()");
+
+ bool UsesWipMatchOpcode = false;
+ for (const auto &Match : MatchPats) {
+ const auto *Pat = Match.second.get();
+
+ if (const auto *CXXPat = dyn_cast<CXXPattern>(Pat)) {
+ if (!CXXPat->getRawCode().contains("return "))
+ PrintWarning("'match' C++ code does not seem to return!");
+ continue;
+ }
+
+ // MIFlags in match cannot use the following syntax: (MIFlags $mi)
+ if (const auto *CGP = dyn_cast<CodeGenInstructionPattern>(Pat)) {
+ if (auto *FI = CGP->getMIFlagsInfo()) {
+ if (!FI->copy_flags().empty()) {
+ PrintError(
+ "'match' patterns cannot refer to flags from other instructions");
+ PrintNote("MIFlags in '" + CGP->getName() +
+ "' refer to: " + join(FI->copy_flags(), ", "));
+ return false;
+ }
+ }
+ }
+
+ const auto *AOP = dyn_cast<AnyOpcodePattern>(Pat);
+ if (!AOP)
+ continue;
+
+ if (UsesWipMatchOpcode) {
+ PrintError("wip_opcode_match can only be present once");
+ return false;
+ }
+
+ UsesWipMatchOpcode = true;
+ }
+
+ for (const auto &Apply : ApplyPats) {
+ assert(Apply.second.get());
+ const auto *IP = dyn_cast<InstructionPattern>(Apply.second.get());
+ if (!IP)
+ continue;
+
+ if (UsesWipMatchOpcode) {
+ PrintError("cannot use wip_match_opcode in combination with apply "
+ "instruction patterns!");
+ return false;
+ }
+
+ // Check that the insts mentioned in copy_flags exist.
+ if (const auto *CGP = dyn_cast<CodeGenInstructionPattern>(IP)) {
+ if (auto *FI = CGP->getMIFlagsInfo()) {
+ for (auto InstName : FI->copy_flags()) {
+ auto It = MatchPats.find(InstName);
+ if (It == MatchPats.end()) {
+ PrintError("unknown instruction '$" + InstName +
+ "' referenced in MIFlags of '" + CGP->getName() + "'");
+ return false;
+ }
+
+ if (!isa<CodeGenInstructionPattern>(It->second.get())) {
+ PrintError(
+ "'$" + InstName +
+ "' does not refer to a CodeGenInstruction in MIFlags of '" +
+ CGP->getName() + "'");
+ return false;
+ }
+ }
+ }
+ }
+
+ const auto *BIP = dyn_cast<BuiltinPattern>(IP);
+ if (!BIP)
+ continue;
+ StringRef Name = BIP->getInstName();
+
+ // (GIEraseInst) has to be the only apply pattern, or it can not be used at
+ // all. The root cannot have any defs either.
+ switch (BIP->getBuiltinKind()) {
+ case BI_EraseRoot: {
+ if (ApplyPats.size() > 1) {
+ PrintError(Name + " must be the only 'apply' pattern");
+ return false;
+ }
+
+ const auto *IRoot = dyn_cast<CodeGenInstructionPattern>(MatchRoot);
+ if (!IRoot) {
+ PrintError(Name +
+ " can only be used if the root is a CodeGenInstruction");
+ return false;
+ }
+
+ if (IRoot->getNumInstDefs() != 0) {
+ PrintError(Name + " can only be used if on roots that do "
+ "not have any output operand");
+ PrintNote("'" + IRoot->getInstName() + "' has " +
+ Twine(IRoot->getNumInstDefs()) + " output operands");
+ return false;
+ }
+ break;
+ }
+ case BI_ReplaceReg: {
+ // (GIReplaceReg can only be used on the root instruction)
+ // TODO: When we allow rewriting non-root instructions, also allow this.
+ StringRef OldRegName = BIP->getOperand(0).getOperandName();
+ auto *Def = MatchOpTable.getDef(OldRegName);
+ if (!Def) {
+ PrintError(Name + " cannot find a matched pattern that defines '" +
+ OldRegName + "'");
+ return false;
+ }
+ if (MatchOpTable.getDef(OldRegName) != MatchRoot) {
+ PrintError(Name + " cannot replace '" + OldRegName +
+ "': this builtin can only replace a register defined by the "
+ "match root");
+ return false;
+ }
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+RuleMatcher &CombineRuleBuilder::addRuleMatcher(const PatternAlternatives &Alts,
+ Twine AdditionalComment) {
+ auto &RM = OutRMs.emplace_back(RuleDef.getLoc());
+ addFeaturePredicates(RM);
+ RM.setPermanentGISelFlags(GISF_IgnoreCopies);
+ RM.addRequiredSimplePredicate(getIsEnabledPredicateEnumName(RuleID));
+
+ std::string Comment;
+ raw_string_ostream CommentOS(Comment);
+ CommentOS << "Combiner Rule #" << RuleID << ": " << RuleDef.getName();
+ if (!Alts.empty()) {
+ CommentOS << " @ ";
+ print(CommentOS, Alts);
+ }
+ if (!AdditionalComment.isTriviallyEmpty())
+ CommentOS << "; " << AdditionalComment;
+ RM.addAction<DebugCommentAction>(Comment);
+ return RM;
+}
+
+bool CombineRuleBuilder::addFeaturePredicates(RuleMatcher &M) {
+ if (!RuleDef.getValue("Predicates"))
+ return true;
+
+ ListInit *Preds = RuleDef.getValueAsListInit("Predicates");
+ for (Init *PI : Preds->getValues()) {
+ DefInit *Pred = dyn_cast<DefInit>(PI);
+ if (!Pred)
+ continue;
+
+ Record *Def = Pred->getDef();
+ if (!Def->isSubClassOf("Predicate")) {
+ ::PrintError(Def, "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;
+}
+
+bool CombineRuleBuilder::findRoots() {
+ const auto Finish = [&]() {
+ assert(MatchRoot);
+
+ if (hasOnlyCXXApplyPatterns() || hasEraseRoot())
+ return true;
+
+ auto *IPRoot = dyn_cast<InstructionPattern>(MatchRoot);
+ if (!IPRoot)
+ return true;
+
+ if (IPRoot->getNumInstDefs() == 0) {
+ // No defs to work with -> find the root using the pattern name.
+ auto It = ApplyPats.find(RootName);
+ if (It == ApplyPats.end()) {
+ PrintError("Cannot find root '" + RootName + "' in apply patterns!");
+ return false;
+ }
+
+ auto *ApplyRoot = dyn_cast<InstructionPattern>(It->second.get());
+ if (!ApplyRoot) {
+ PrintError("apply pattern root '" + RootName +
+ "' must be an instruction pattern");
+ return false;
+ }
+
+ ApplyRoots.insert(ApplyRoot);
+ return true;
+ }
+
+ // Collect all redefinitions of the MatchRoot's defs and put them in
+ // ApplyRoots.
+ const auto DefsNeeded = IPRoot->getApplyDefsNeeded();
+ for (auto &Op : DefsNeeded) {
+ assert(Op.isDef() && Op.isNamedOperand());
+ StringRef Name = Op.getOperandName();
+
+ auto *ApplyRedef = ApplyOpTable.getDef(Name);
+ if (!ApplyRedef) {
+ PrintError("'" + Name + "' must be redefined in the 'apply' pattern");
+ return false;
+ }
+
+ ApplyRoots.insert((InstructionPattern *)ApplyRedef);
+ }
+
+ if (auto It = ApplyPats.find(RootName); It != ApplyPats.end()) {
+ if (find(ApplyRoots, It->second.get()) == ApplyRoots.end()) {
+ PrintError("apply pattern '" + RootName +
+ "' is supposed to be a root but it does not redefine any of "
+ "the defs of the match root");
+ return false;
+ }
+ }
+
+ return true;
+ };
+
+ // Look by pattern name, e.g.
+ // (G_FNEG $x, $y):$root
+ if (auto MatchPatIt = MatchPats.find(RootName);
+ MatchPatIt != MatchPats.end()) {
+ MatchRoot = MatchPatIt->second.get();
+ return Finish();
+ }
+
+ // Look by def:
+ // (G_FNEG $root, $y)
+ auto LookupRes = MatchOpTable.lookup(RootName);
+ if (!LookupRes.Found) {
+ PrintError("Cannot find root '" + RootName + "' in match patterns!");
+ return false;
+ }
+
+ MatchRoot = LookupRes.Def;
+ if (!MatchRoot) {
+ PrintError("Cannot use live-in operand '" + RootName +
+ "' as match pattern root!");
+ return false;
+ }
+
+ return Finish();
+}
+
+bool CombineRuleBuilder::buildRuleOperandsTable() {
+ const auto DiagnoseRedefMatch = [&](StringRef OpName) {
+ PrintError("Operand '" + OpName +
+ "' is defined multiple times in the 'match' patterns");
+ };
+
+ const auto DiagnoseRedefApply = [&](StringRef OpName) {
+ PrintError("Operand '" + OpName +
+ "' is defined multiple times in the 'apply' patterns");
+ };
+
+ for (auto &Pat : values(MatchPats)) {
+ auto *IP = dyn_cast<InstructionPattern>(Pat.get());
+ if (IP && !MatchOpTable.addPattern(IP, DiagnoseRedefMatch))
+ return false;
+ }
+
+ for (auto &Pat : values(ApplyPats)) {
+ auto *IP = dyn_cast<InstructionPattern>(Pat.get());
+ if (IP && !ApplyOpTable.addPattern(IP, DiagnoseRedefApply))
+ return false;
+ }
+
+ return true;
+}
+
+bool CombineRuleBuilder::parseDefs(const DagInit &Def) {
+ if (Def.getOperatorAsDef(RuleDef.getLoc())->getName() != "defs") {
+ PrintError("Expected defs operator");
+ return false;
+ }
+
+ SmallVector<StringRef> 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::parsePatternList(
+ const DagInit &List,
+ function_ref<bool(std::unique_ptr<Pattern>)> ParseAction,
+ StringRef Operator, ArrayRef<SMLoc> DiagLoc,
+ StringRef AnonPatNamePrefix) const {
+ if (List.getOperatorAsDef(RuleDef.getLoc())->getName() != Operator) {
+ ::PrintError(DiagLoc, "Expected " + Operator + " operator");
+ return false;
+ }
+
+ if (List.getNumArgs() == 0) {
+ ::PrintError(DiagLoc, Operator + " pattern list 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.
+ for (unsigned I = 0; I < List.getNumArgs(); ++I) {
+ Init *Arg = List.getArg(I);
+ std::string Name = List.getArgName(I)
+ ? List.getArgName(I)->getValue().str()
+ : ("__" + AnonPatNamePrefix + "_" + Twine(I)).str();
+
+ if (auto Pat = parseInstructionPattern(*Arg, Name)) {
+ if (!ParseAction(std::move(Pat)))
+ return false;
+ continue;
+ }
+
+ if (auto Pat = parseWipMatchOpcodeMatcher(*Arg, Name)) {
+ if (!ParseAction(std::move(Pat)))
+ return false;
+ continue;
+ }
+
+ // Parse arbitrary C++ code
+ if (const auto *StringI = dyn_cast<StringInit>(Arg)) {
+ auto CXXPat = std::make_unique<CXXPattern>(*StringI, insertStrRef(Name));
+ if (!ParseAction(std::move(CXXPat)))
+ return false;
+ continue;
+ }
+
+ ::PrintError(DiagLoc,
+ "Failed to parse pattern: '" + Arg->getAsString() + "'");
+ return false;
+ }
+
+ return true;
+}
+
+std::unique_ptr<Pattern>
+CombineRuleBuilder::parseInstructionPattern(const Init &Arg,
+ StringRef Name) const {
+ const DagInit *DagPat = dyn_cast<DagInit>(&Arg);
+ if (!DagPat)
+ return nullptr;
+
+ std::unique_ptr<InstructionPattern> Pat;
+ if (const DagInit *IP = getDagWithOperatorOfSubClass(Arg, "Instruction")) {
+ auto &Instr = CGT.getInstruction(IP->getOperatorAsDef(RuleDef.getLoc()));
+ Pat =
+ std::make_unique<CodeGenInstructionPattern>(Instr, insertStrRef(Name));
+ } else if (const DagInit *PFP =
+ getDagWithOperatorOfSubClass(Arg, PatFrag::ClassName)) {
+ const Record *Def = PFP->getOperatorAsDef(RuleDef.getLoc());
+ const PatFrag *PF = parsePatFrag(Def);
+ if (!PF)
+ return nullptr; // Already diagnosed by parsePatFrag
+ Pat = std::make_unique<PatFragPattern>(*PF, insertStrRef(Name));
+ } else if (const DagInit *BP =
+ getDagWithOperatorOfSubClass(Arg, BuiltinPattern::ClassName)) {
+ Pat = std::make_unique<BuiltinPattern>(
+ *BP->getOperatorAsDef(RuleDef.getLoc()), insertStrRef(Name));
+ } else {
+ return nullptr;
+ }
+
+ for (unsigned K = 0; K < DagPat->getNumArgs(); ++K) {
+ Init *Arg = DagPat->getArg(K);
+ if (auto *DagArg = getDagWithSpecificOperator(*Arg, "MIFlags")) {
+ if (!parseInstructionPatternMIFlags(*Pat, DagArg))
+ return nullptr;
+ continue;
+ }
+
+ if (!parseInstructionPatternOperand(*Pat, Arg, DagPat->getArgName(K)))
+ return nullptr;
+ }
+
+ if (!Pat->checkSemantics(RuleDef.getLoc()))
+ return nullptr;
+
+ return std::move(Pat);
+}
+
+std::unique_ptr<Pattern>
+CombineRuleBuilder::parseWipMatchOpcodeMatcher(const Init &Arg,
+ StringRef Name) const {
+ 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<AnyOpcodePattern>(insertStrRef(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::parseInstructionPatternOperand(
+ InstructionPattern &IP, const Init *OpInit,
+ const StringInit *OpName) const {
+ const auto ParseErr = [&]() {
+ PrintError("cannot parse operand '" + OpInit->getAsUnquotedString() + "' ");
+ if (OpName)
+ PrintNote("operand name is '" + OpName->getAsUnquotedString() + "'");
+ return false;
+ };
+
+ // untyped immediate, e.g. 0
+ if (const auto *IntImm = dyn_cast<IntInit>(OpInit)) {
+ std::string Name = OpName ? OpName->getAsUnquotedString() : "";
+ IP.addOperand(IntImm->getValue(), insertStrRef(Name), PatternType());
+ return true;
+ }
+
+ // typed immediate, e.g. (i32 0)
+ if (const auto *DagOp = dyn_cast<DagInit>(OpInit)) {
+ if (DagOp->getNumArgs() != 1)
+ return ParseErr();
+
+ const Record *TyDef = DagOp->getOperatorAsDef(RuleDef.getLoc());
+ auto ImmTy = PatternType::get(RuleDef.getLoc(), TyDef,
+ "cannot parse immediate '" +
+ DagOp->getAsUnquotedString() + "'");
+ if (!ImmTy)
+ return false;
+
+ if (!IP.hasAllDefs()) {
+ PrintError("out operand of '" + IP.getInstName() +
+ "' cannot be an immediate");
+ return false;
+ }
+
+ const auto *Val = dyn_cast<IntInit>(DagOp->getArg(0));
+ if (!Val)
+ return ParseErr();
+
+ std::string Name = OpName ? OpName->getAsUnquotedString() : "";
+ IP.addOperand(Val->getValue(), insertStrRef(Name), *ImmTy);
+ return true;
+ }
+
+ // Typed operand e.g. $x/$z in (G_FNEG $x, $z)
+ if (auto *DefI = dyn_cast<DefInit>(OpInit)) {
+ if (!OpName) {
+ PrintError("expected an operand name after '" + OpInit->getAsString() +
+ "'");
+ return false;
+ }
+ const Record *Def = DefI->getDef();
+ auto Ty =
+ PatternType::get(RuleDef.getLoc(), Def, "cannot parse operand type");
+ if (!Ty)
+ return false;
+ IP.addOperand(insertStrRef(OpName->getAsUnquotedString()), *Ty);
+ return true;
+ }
+
+ // Untyped operand e.g. $x/$z in (G_FNEG $x, $z)
+ if (isa<UnsetInit>(OpInit)) {
+ assert(OpName && "Unset w/ no OpName?");
+ IP.addOperand(insertStrRef(OpName->getAsUnquotedString()), PatternType());
+ return true;
+ }
+
+ return ParseErr();
+}
+
+bool CombineRuleBuilder::parseInstructionPatternMIFlags(
+ InstructionPattern &IP, const DagInit *Op) const {
+ auto *CGIP = dyn_cast<CodeGenInstructionPattern>(&IP);
+ if (!CGIP) {
+ PrintError("matching/writing MIFlags is only allowed on CodeGenInstruction "
+ "patterns");
+ return false;
+ }
+
+ const auto CheckFlagEnum = [&](const Record *R) {
+ if (!R->isSubClassOf(MIFlagsEnumClassName)) {
+ PrintError("'" + R->getName() + "' is not a subclass of '" +
+ MIFlagsEnumClassName + "'");
+ return false;
+ }
+
+ return true;
+ };
+
+ if (CGIP->getMIFlagsInfo()) {
+ PrintError("MIFlags can only be present once on an instruction");
+ return false;
+ }
+
+ auto &FI = CGIP->getOrCreateMIFlagsInfo();
+ for (unsigned K = 0; K < Op->getNumArgs(); ++K) {
+ const Init *Arg = Op->getArg(K);
+
+ // Match/set a flag: (MIFlags FmNoNans)
+ if (const auto *Def = dyn_cast<DefInit>(Arg)) {
+ const Record *R = Def->getDef();
+ if (!CheckFlagEnum(R))
+ return false;
+
+ FI.addSetFlag(R);
+ continue;
+ }
+
+ // Do not match a flag/unset a flag: (MIFlags (not FmNoNans))
+ if (const DagInit *NotDag = getDagWithSpecificOperator(*Arg, "not")) {
+ for (const Init *NotArg : NotDag->getArgs()) {
+ const DefInit *DefArg = dyn_cast<DefInit>(NotArg);
+ if (!DefArg) {
+ PrintError("cannot parse '" + NotArg->getAsUnquotedString() +
+ "': expected a '" + MIFlagsEnumClassName + "'");
+ return false;
+ }
+
+ const Record *R = DefArg->getDef();
+ if (!CheckFlagEnum(R))
+ return false;
+
+ FI.addUnsetFlag(R);
+ continue;
+ }
+
+ continue;
+ }
+
+ // Copy flags from a matched instruction: (MIFlags $mi)
+ if (isa<UnsetInit>(Arg)) {
+ FI.addCopyFlag(insertStrRef(Op->getArgName(K)->getAsUnquotedString()));
+ continue;
+ }
+ }
+
+ return true;
+}
+
+std::unique_ptr<PatFrag>
+CombineRuleBuilder::parsePatFragImpl(const Record *Def) const {
+ auto StackTrace = PrettyStackTraceParse(*Def);
+ if (!Def->isSubClassOf(PatFrag::ClassName))
+ return nullptr;
+
+ const DagInit *Ins = Def->getValueAsDag("InOperands");
+ if (Ins->getOperatorAsDef(Def->getLoc())->getName() != "ins") {
+ ::PrintError(Def, "expected 'ins' operator for " + PatFrag::ClassName +
+ " in operands list");
+ return nullptr;
+ }
+
+ const DagInit *Outs = Def->getValueAsDag("OutOperands");
+ if (Outs->getOperatorAsDef(Def->getLoc())->getName() != "outs") {
+ ::PrintError(Def, "expected 'outs' operator for " + PatFrag::ClassName +
+ " out operands list");
+ return nullptr;
+ }
+
+ auto Result = std::make_unique<PatFrag>(*Def);
+ if (!parsePatFragParamList(Def->getLoc(), *Outs,
+ [&](StringRef Name, PatFrag::ParamKind Kind) {
+ Result->addOutParam(insertStrRef(Name), Kind);
+ return true;
+ }))
+ return nullptr;
+
+ if (!parsePatFragParamList(Def->getLoc(), *Ins,
+ [&](StringRef Name, PatFrag::ParamKind Kind) {
+ Result->addInParam(insertStrRef(Name), Kind);
+ return true;
+ }))
+ return nullptr;
+
+ const ListInit *Alts = Def->getValueAsListInit("Alternatives");
+ unsigned AltIdx = 0;
+ for (const Init *Alt : *Alts) {
+ const auto *PatDag = dyn_cast<DagInit>(Alt);
+ if (!PatDag) {
+ ::PrintError(Def, "expected dag init for PatFrag pattern alternative");
+ return nullptr;
+ }
+
+ PatFrag::Alternative &A = Result->addAlternative();
+ const auto AddPat = [&](std::unique_ptr<Pattern> Pat) {
+ A.Pats.push_back(std::move(Pat));
+ return true;
+ };
+
+ if (!parsePatternList(
+ *PatDag, AddPat, "pattern", Def->getLoc(),
+ /*AnonPatPrefix*/
+ (Def->getName() + "_alt" + Twine(AltIdx++) + "_pattern").str()))
+ return nullptr;
+ }
+
+ if (!Result->buildOperandsTables() || !Result->checkSemantics())
+ return nullptr;
+
+ return Result;
+}
+
+bool CombineRuleBuilder::parsePatFragParamList(
+ ArrayRef<SMLoc> DiagLoc, const DagInit &OpsList,
+ function_ref<bool(StringRef, PatFrag::ParamKind)> ParseAction) const {
+ for (unsigned K = 0; K < OpsList.getNumArgs(); ++K) {
+ const StringInit *Name = OpsList.getArgName(K);
+ const Init *Ty = OpsList.getArg(K);
+
+ if (!Name) {
+ ::PrintError(DiagLoc, "all operands must be named'");
+ return false;
+ }
+ const std::string NameStr = Name->getAsUnquotedString();
+
+ PatFrag::ParamKind OpKind;
+ if (isSpecificDef(*Ty, "gi_imm"))
+ OpKind = PatFrag::PK_Imm;
+ else if (isSpecificDef(*Ty, "root"))
+ OpKind = PatFrag::PK_Root;
+ else if (isa<UnsetInit>(Ty) ||
+ isSpecificDef(*Ty, "gi_mo")) // no type = gi_mo.
+ OpKind = PatFrag::PK_MachineOperand;
+ else {
+ ::PrintError(
+ DiagLoc,
+ "'" + NameStr +
+ "' operand type was expected to be 'root', 'gi_imm' or 'gi_mo'");
+ return false;
+ }
+
+ if (!ParseAction(NameStr, OpKind))
+ return false;
+ }
+
+ return true;
+}
+
+const PatFrag *CombineRuleBuilder::parsePatFrag(const Record *Def) const {
+ // Cache already parsed PatFrags to avoid doing extra work.
+ static DenseMap<const Record *, std::unique_ptr<PatFrag>> ParsedPatFrags;
+
+ auto It = ParsedPatFrags.find(Def);
+ if (It != ParsedPatFrags.end()) {
+ SeenPatFrags.insert(It->second.get());
+ return It->second.get();
+ }
+
+ std::unique_ptr<PatFrag> NewPatFrag = parsePatFragImpl(Def);
+ if (!NewPatFrag) {
+ ::PrintError(Def, "Could not parse " + PatFrag::ClassName + " '" +
+ Def->getName() + "'");
+ // Put a nullptr in the map so we don't attempt parsing this again.
+ ParsedPatFrags[Def] = nullptr;
+ return nullptr;
+ }
+
+ const auto *Res = NewPatFrag.get();
+ ParsedPatFrags[Def] = std::move(NewPatFrag);
+ SeenPatFrags.insert(Res);
+ return Res;
+}
+
+bool CombineRuleBuilder::emitMatchPattern(CodeExpansions &CE,
+ const PatternAlternatives &Alts,
+ const InstructionPattern &IP) {
+ auto StackTrace = PrettyStackTraceEmit(RuleDef, &IP);
+
+ auto &M = addRuleMatcher(Alts);
+ InstructionMatcher &IM = M.addInstructionMatcher(IP.getName());
+ declareInstExpansion(CE, IM, IP.getName());
+
+ DenseSet<const Pattern *> SeenPats;
+
+ const auto FindOperandDef = [&](StringRef Op) -> InstructionPattern * {
+ return MatchOpTable.getDef(Op);
+ };
+
+ if (const auto *CGP = dyn_cast<CodeGenInstructionPattern>(&IP)) {
+ if (!emitCodeGenInstructionMatchPattern(CE, Alts, M, IM, *CGP, SeenPats,
+ FindOperandDef))
+ return false;
+ } else if (const auto *PFP = dyn_cast<PatFragPattern>(&IP)) {
+ if (!PFP->getPatFrag().canBeMatchRoot()) {
+ PrintError("cannot use '" + PFP->getInstName() + " as match root");
+ return false;
+ }
+
+ if (!emitPatFragMatchPattern(CE, Alts, M, &IM, *PFP, SeenPats))
+ return false;
+ } else if (isa<BuiltinPattern>(&IP)) {
+ llvm_unreachable("No match builtins known!");
+ } else
+ llvm_unreachable("Unknown kind of InstructionPattern!");
+
+ // 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_PatFrag: {
+ if (!emitPatFragMatchPattern(CE, Alts, M, /*IM*/ nullptr,
+ *cast<PatFragPattern>(Pat.get()), SeenPats))
+ return false;
+ continue;
+ }
+ case Pattern::K_Builtin:
+ PrintError("No known match builtins");
+ return false;
+ case Pattern::K_CodeGenInstruction:
+ cast<InstructionPattern>(Pat.get())->reportUnreachable(RuleDef.getLoc());
+ return false;
+ case Pattern::K_CXX: {
+ addCXXPredicate(M, CE, *cast<CXXPattern>(Pat.get()), Alts);
+ continue;
+ }
+ default:
+ llvm_unreachable("unknown pattern kind!");
+ }
+ }
+
+ return emitApplyPatterns(CE, M);
+}
+
+bool CombineRuleBuilder::emitMatchPattern(CodeExpansions &CE,
+ const PatternAlternatives &Alts,
+ const AnyOpcodePattern &AOP) {
+ auto StackTrace = PrettyStackTraceEmit(RuleDef, &AOP);
+
+ for (const CodeGenInstruction *CGI : AOP.insts()) {
+ auto &M = addRuleMatcher(Alts, "wip_match_opcode '" +
+ 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<InstructionOpcodeMatcher>(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_PatFrag: {
+ DenseSet<const Pattern *> SeenPats;
+ if (!emitPatFragMatchPattern(CE, Alts, M, /*IM*/ nullptr,
+ *cast<PatFragPattern>(Pat.get()),
+ SeenPats))
+ return false;
+ continue;
+ }
+ case Pattern::K_Builtin:
+ PrintError("No known match builtins");
+ return false;
+ case Pattern::K_CodeGenInstruction:
+ cast<InstructionPattern>(Pat.get())->reportUnreachable(
+ RuleDef.getLoc());
+ return false;
+ case Pattern::K_CXX: {
+ addCXXPredicate(M, CE, *cast<CXXPattern>(Pat.get()), Alts);
+ break;
+ }
+ default:
+ llvm_unreachable("unknown pattern kind!");
+ }
+ }
+
+ if (!emitApplyPatterns(CE, M))
+ return false;
+ }
+
+ return true;
+}
+
+bool CombineRuleBuilder::emitPatFragMatchPattern(
+ CodeExpansions &CE, const PatternAlternatives &Alts, RuleMatcher &RM,
+ InstructionMatcher *IM, const PatFragPattern &PFP,
+ DenseSet<const Pattern *> &SeenPats) {
+ auto StackTrace = PrettyStackTraceEmit(RuleDef, &PFP);
+
+ if (SeenPats.contains(&PFP))
+ return true;
+ SeenPats.insert(&PFP);
+
+ const auto &PF = PFP.getPatFrag();
+
+ if (!IM) {
+ // When we don't have an IM, this means this PatFrag isn't reachable from
+ // the root. This is only acceptable if it doesn't define anything (e.g. a
+ // pure C++ PatFrag).
+ if (PF.num_out_params() != 0) {
+ PFP.reportUnreachable(RuleDef.getLoc());
+ return false;
+ }
+ } else {
+ // When an IM is provided, this is reachable from the root, and we're
+ // expecting to have output operands.
+ // TODO: If we want to allow for multiple roots we'll need a map of IMs
+ // then, and emission becomes a bit more complicated.
+ assert(PF.num_roots() == 1);
+ }
+
+ CodeExpansions PatFragCEs;
+ if (!PFP.mapInputCodeExpansions(CE, PatFragCEs, RuleDef.getLoc()))
+ return false;
+
+ // List of {ParamName, ArgName}.
+ // When all patterns have been emitted, find expansions in PatFragCEs named
+ // ArgName and add their expansion to CE using ParamName as the key.
+ SmallVector<std::pair<std::string, std::string>, 4> CEsToImport;
+
+ // Map parameter names to the actual argument.
+ const auto OperandMapper =
+ [&](const InstructionOperand &O) -> InstructionOperand {
+ if (!O.isNamedOperand())
+ return O;
+
+ StringRef ParamName = O.getOperandName();
+
+ // Not sure what to do with those tbh. They should probably never be here.
+ assert(!O.isNamedImmediate() && "TODO: handle named imms");
+ unsigned PIdx = PF.getParamIdx(ParamName);
+
+ // Map parameters to the argument values.
+ if (PIdx == (unsigned)-1) {
+ // This is a temp of the PatFragPattern, prefix the name to avoid
+ // conflicts.
+ return O.withNewName(
+ insertStrRef((PFP.getName() + "." + ParamName).str()));
+ }
+
+ // The operand will be added to PatFragCEs's code expansions using the
+ // parameter's name. If it's bound to some operand during emission of the
+ // patterns, we'll want to add it to CE.
+ auto ArgOp = PFP.getOperand(PIdx);
+ if (ArgOp.isNamedOperand())
+ CEsToImport.emplace_back(ArgOp.getOperandName().str(), ParamName);
+
+ if (ArgOp.getType() && O.getType() && ArgOp.getType() != O.getType()) {
+ StringRef PFName = PF.getName();
+ PrintWarning("impossible type constraints: operand " + Twine(PIdx) +
+ " of '" + PFP.getName() + "' has type '" +
+ ArgOp.getType().str() + "', but '" + PFName +
+ "' constrains it to '" + O.getType().str() + "'");
+ if (ArgOp.isNamedOperand())
+ PrintNote("operand " + Twine(PIdx) + " of '" + PFP.getName() +
+ "' is '" + ArgOp.getOperandName() + "'");
+ if (O.isNamedOperand())
+ PrintNote("argument " + Twine(PIdx) + " of '" + PFName + "' is '" +
+ ParamName + "'");
+ }
+
+ return ArgOp;
+ };
+
+ // PatFragPatterns are only made of InstructionPatterns or CXXPatterns.
+ // Emit instructions from the root.
+ const auto &FragAlt = PF.getAlternative(Alts.lookup(&PFP));
+ const auto &FragAltOT = FragAlt.OpTable;
+ const auto LookupOperandDef =
+ [&](StringRef Op) -> const InstructionPattern * {
+ return FragAltOT.getDef(Op);
+ };
+
+ DenseSet<const Pattern *> PatFragSeenPats;
+ for (const auto &[Idx, InOp] : enumerate(PF.out_params())) {
+ if (InOp.Kind != PatFrag::PK_Root)
+ continue;
+
+ StringRef ParamName = InOp.Name;
+ const auto *Def = FragAltOT.getDef(ParamName);
+ assert(Def && "PatFrag::checkSemantics should have emitted an error if "
+ "an out operand isn't defined!");
+ assert(isa<CodeGenInstructionPattern>(Def) &&
+ "Nested PatFrags not supported yet");
+
+ if (!emitCodeGenInstructionMatchPattern(
+ PatFragCEs, Alts, RM, *IM, *cast<CodeGenInstructionPattern>(Def),
+ PatFragSeenPats, LookupOperandDef, OperandMapper))
+ return false;
+ }
+
+ // Emit leftovers.
+ for (const auto &Pat : FragAlt.Pats) {
+ if (PatFragSeenPats.contains(Pat.get()))
+ continue;
+
+ if (const auto *CXXPat = dyn_cast<CXXPattern>(Pat.get())) {
+ addCXXPredicate(RM, PatFragCEs, *CXXPat, Alts);
+ continue;
+ }
+
+ if (const auto *IP = dyn_cast<InstructionPattern>(Pat.get())) {
+ IP->reportUnreachable(PF.getLoc());
+ return false;
+ }
+
+ llvm_unreachable("Unexpected pattern kind in PatFrag");
+ }
+
+ for (const auto &[ParamName, ArgName] : CEsToImport) {
+ // Note: we're find if ParamName already exists. It just means it's been
+ // bound before, so we prefer to keep the first binding.
+ CE.declare(ParamName, PatFragCEs.lookup(ArgName));
+ }
+
+ return true;
+}
+
+bool CombineRuleBuilder::emitApplyPatterns(CodeExpansions &CE, RuleMatcher &M) {
+ if (hasOnlyCXXApplyPatterns()) {
+ for (auto &Pat : values(ApplyPats))
+ addCXXAction(M, CE, *cast<CXXPattern>(Pat.get()));
+ return true;
+ }
+
+ DenseSet<const Pattern *> SeenPats;
+ StringMap<unsigned> OperandToTempRegID;
+
+ for (auto *ApplyRoot : ApplyRoots) {
+ assert(isa<InstructionPattern>(ApplyRoot) &&
+ "Root can only be a InstructionPattern!");
+ if (!emitInstructionApplyPattern(CE, M,
+ cast<InstructionPattern>(*ApplyRoot),
+ SeenPats, OperandToTempRegID))
+ return false;
+ }
+
+ for (auto &Pat : values(ApplyPats)) {
+ if (SeenPats.contains(Pat.get()))
+ continue;
+
+ switch (Pat->getKind()) {
+ case Pattern::K_AnyOpcode:
+ llvm_unreachable("Unexpected pattern in apply!");
+ case Pattern::K_PatFrag:
+ // TODO: We could support pure C++ PatFrags as a temporary thing.
+ llvm_unreachable("Unexpected pattern in apply!");
+ case Pattern::K_Builtin:
+ if (!emitInstructionApplyPattern(CE, M, cast<BuiltinPattern>(*Pat),
+ SeenPats, OperandToTempRegID))
+ return false;
+ break;
+ case Pattern::K_CodeGenInstruction:
+ cast<CodeGenInstructionPattern>(*Pat).reportUnreachable(RuleDef.getLoc());
+ return false;
+ case Pattern::K_CXX: {
+ addCXXAction(M, CE, *cast<CXXPattern>(Pat.get()));
+ continue;
+ }
+ default:
+ llvm_unreachable("unknown pattern kind!");
+ }
+ }
+
+ return true;
+}
+
+bool CombineRuleBuilder::emitInstructionApplyPattern(
+ CodeExpansions &CE, RuleMatcher &M, const InstructionPattern &P,
+ DenseSet<const Pattern *> &SeenPats,
+ StringMap<unsigned> &OperandToTempRegID) {
+ auto StackTrace = PrettyStackTraceEmit(RuleDef, &P);
+
+ if (SeenPats.contains(&P))
+ return true;
+
+ SeenPats.insert(&P);
+
+ // First, render the uses.
+ for (auto &Op : P.named_operands()) {
+ if (Op.isDef())
+ continue;
+
+ StringRef OpName = Op.getOperandName();
+ if (const auto *DefPat = ApplyOpTable.getDef(OpName)) {
+ if (!emitInstructionApplyPattern(CE, M, *DefPat, SeenPats,
+ OperandToTempRegID))
+ return false;
+ } else {
+ // If we have no def, check this exists in the MatchRoot.
+ if (!Op.isNamedImmediate() && !MatchOpTable.lookup(OpName).Found) {
+ PrintError("invalid output operand '" + OpName +
+ "': operand is not a live-in of the match pattern, and it "
+ "has no definition");
+ return false;
+ }
+ }
+ }
+
+ if (const auto *BP = dyn_cast<BuiltinPattern>(&P))
+ return emitBuiltinApplyPattern(CE, M, *BP, OperandToTempRegID);
+
+ if (isa<PatFragPattern>(&P))
+ llvm_unreachable("PatFragPatterns is not supported in 'apply'!");
+
+ auto &CGIP = cast<CodeGenInstructionPattern>(P);
+
+ // Now render this inst.
+ auto &DstMI =
+ M.addAction<BuildMIAction>(M.allocateOutputInsnID(), &CGIP.getInst());
+
+ for (auto &Op : P.operands()) {
+ if (Op.isNamedImmediate()) {
+ PrintError("invalid output operand '" + Op.getOperandName() +
+ "': output immediates cannot be named");
+ PrintNote("while emitting pattern '" + P.getName() + "' (" +
+ P.getInstName() + ")");
+ return false;
+ }
+
+ if (Op.hasImmValue()) {
+ if (!emitCodeGenInstructionApplyImmOperand(M, DstMI, CGIP, Op))
+ return false;
+ continue;
+ }
+
+ StringRef OpName = Op.getOperandName();
+
+ // Uses of operand.
+ if (!Op.isDef()) {
+ if (auto It = OperandToTempRegID.find(OpName);
+ It != OperandToTempRegID.end()) {
+ assert(!MatchOpTable.lookup(OpName).Found &&
+ "Temp reg is also from match pattern?");
+ DstMI.addRenderer<TempRegRenderer>(It->second);
+ } else {
+ // This should be a match live in or a redef of a matched instr.
+ // If it's a use of a temporary register, then we messed up somewhere -
+ // the previous condition should have passed.
+ assert(MatchOpTable.lookup(OpName).Found &&
+ !ApplyOpTable.getDef(OpName) && "Temp reg not emitted yet!");
+ DstMI.addRenderer<CopyRenderer>(OpName);
+ }
+ continue;
+ }
+
+ // Determine what we're dealing with. Are we replace a matched instruction?
+ // Creating a new one?
+ auto OpLookupRes = MatchOpTable.lookup(OpName);
+ if (OpLookupRes.Found) {
+ if (OpLookupRes.isLiveIn()) {
+ // live-in of the match pattern.
+ PrintError("Cannot define live-in operand '" + OpName +
+ "' in the 'apply' pattern");
+ return false;
+ }
+ assert(OpLookupRes.Def);
+
+ // TODO: Handle this. We need to mutate the instr, or delete the old
+ // one.
+ // Likewise, we also need to ensure we redef everything, if the
+ // instr has more than one def, we need to redef all or nothing.
+ if (OpLookupRes.Def != MatchRoot) {
+ PrintError("redefining an instruction other than the root is not "
+ "supported (operand '" +
+ OpName + "')");
+ return false;
+ }
+ // redef of a match
+ DstMI.addRenderer<CopyRenderer>(OpName);
+ continue;
+ }
+
+ // Define a new register unique to the apply patterns (AKA a "temp"
+ // register).
+ unsigned TempRegID;
+ if (auto It = OperandToTempRegID.find(OpName);
+ It != OperandToTempRegID.end()) {
+ TempRegID = It->second;
+ } else {
+ // This is a brand new register.
+ TempRegID = M.allocateTempRegID();
+ OperandToTempRegID[OpName] = TempRegID;
+ const auto Ty = Op.getType();
+ if (!Ty) {
+ PrintError("def of a new register '" + OpName +
+ "' in the apply patterns must have a type");
+ return false;
+ }
+
+ declareTempRegExpansion(CE, TempRegID, OpName);
+ // Always insert the action at the beginning, otherwise we may end up
+ // using the temp reg before it's available.
+ M.insertAction<MakeTempRegisterAction>(
+ M.actions_begin(), getLLTCodeGenOrTempType(Ty, M), TempRegID);
+ }
+
+ DstMI.addRenderer<TempRegRenderer>(TempRegID);
+ }
+
+ // Render MIFlags
+ if (const auto *FI = CGIP.getMIFlagsInfo()) {
+ for (StringRef InstName : FI->copy_flags())
+ DstMI.addCopiedMIFlags(M.getInstructionMatcher(InstName));
+ for (StringRef F : FI->set_flags())
+ DstMI.addSetMIFlags(F);
+ for (StringRef F : FI->unset_flags())
+ DstMI.addUnsetMIFlags(F);
+ }
+
+ // Don't allow mutating opcodes for GISel combiners. We want a more precise
+ // handling of MIFlags so we require them to be explicitly preserved.
+ //
+ // TODO: We don't mutate very often, if at all in combiners, but it'd be nice
+ // to re-enable this. We'd then need to always clear MIFlags when mutating
+ // opcodes, and never mutate an inst that we copy flags from.
+ // DstMI.chooseInsnToMutate(M);
+ declareInstExpansion(CE, DstMI, P.getName());
+
+ return true;
+}
+
+bool CombineRuleBuilder::emitCodeGenInstructionApplyImmOperand(
+ RuleMatcher &M, BuildMIAction &DstMI, const CodeGenInstructionPattern &P,
+ const InstructionOperand &O) {
+ // If we have a type, we implicitly emit a G_CONSTANT, except for G_CONSTANT
+ // itself where we emit a CImm.
+ //
+ // No type means we emit a simple imm.
+ // G_CONSTANT is a special case and needs a CImm though so this is likely a
+ // mistake.
+ const bool isGConstant = P.is("G_CONSTANT");
+ const auto Ty = O.getType();
+ if (!Ty) {
+ if (isGConstant) {
+ PrintError("'G_CONSTANT' immediate must be typed!");
+ PrintNote("while emitting pattern '" + P.getName() + "' (" +
+ P.getInstName() + ")");
+ return false;
+ }
+
+ DstMI.addRenderer<ImmRenderer>(O.getImmValue());
+ return true;
+ }
+
+ auto ImmTy = getLLTCodeGenOrTempType(Ty, M);
+
+ if (isGConstant) {
+ DstMI.addRenderer<ImmRenderer>(O.getImmValue(), ImmTy);
+ return true;
+ }
+
+ unsigned TempRegID = M.allocateTempRegID();
+ // Ensure MakeTempReg & the BuildConstantAction occur at the beginning.
+ auto InsertIt = M.insertAction<MakeTempRegisterAction>(M.actions_begin(),
+ ImmTy, TempRegID);
+ M.insertAction<BuildConstantAction>(++InsertIt, TempRegID, O.getImmValue());
+ DstMI.addRenderer<TempRegRenderer>(TempRegID);
+ return true;
+}
+
+bool CombineRuleBuilder::emitBuiltinApplyPattern(
+ CodeExpansions &CE, RuleMatcher &M, const BuiltinPattern &P,
+ StringMap<unsigned> &OperandToTempRegID) {
+ const auto Error = [&](Twine Reason) {
+ PrintError("cannot emit '" + P.getInstName() + "' builtin: " + Reason);
+ return false;
+ };
+
+ switch (P.getBuiltinKind()) {
+ case BI_EraseRoot: {
+ // Root is always inst 0.
+ M.addAction<EraseInstAction>(/*InsnID*/ 0);
+ return true;
+ }
+ case BI_ReplaceReg: {
+ StringRef Old = P.getOperand(0).getOperandName();
+ StringRef New = P.getOperand(1).getOperandName();
+
+ if (!ApplyOpTable.lookup(New).Found && !MatchOpTable.lookup(New).Found)
+ return Error("unknown operand '" + Old + "'");
+
+ auto &OldOM = M.getOperandMatcher(Old);
+ if (auto It = OperandToTempRegID.find(New);
+ It != OperandToTempRegID.end()) {
+ // Replace with temp reg.
+ M.addAction<ReplaceRegAction>(OldOM.getInsnVarID(), OldOM.getOpIdx(),
+ It->second);
+ } else {
+ // Replace with matched reg.
+ auto &NewOM = M.getOperandMatcher(New);
+ M.addAction<ReplaceRegAction>(OldOM.getInsnVarID(), OldOM.getOpIdx(),
+ NewOM.getInsnVarID(), NewOM.getOpIdx());
+ }
+ // checkSemantics should have ensured that we can only rewrite the root.
+ // Ensure we're deleting it.
+ assert(MatchOpTable.getDef(Old) == MatchRoot);
+ // TODO: We could avoid adding the action again if it's already in. The
+ // MatchTable is smart enough to only emit one opcode even if
+ // EraseInstAction is present multiple times. I think searching for a copy
+ // is more expensive than just blindly adding it though.
+ M.addAction<EraseInstAction>(/*InsnID*/ 0);
+
+ return true;
+ }
+ }
+
+ llvm_unreachable("Unknown BuiltinKind!");
+}
+
+bool isLiteralImm(const InstructionPattern &P, unsigned OpIdx) {
+ if (const auto *CGP = dyn_cast<CodeGenInstructionPattern>(&P)) {
+ StringRef InstName = CGP->getInst().TheDef->getName();
+ return (InstName == "G_CONSTANT" || InstName == "G_FCONSTANT") &&
+ OpIdx == 1;
+ }
+
+ llvm_unreachable("TODO");
+}
+
+bool CombineRuleBuilder::emitCodeGenInstructionMatchPattern(
+ CodeExpansions &CE, const PatternAlternatives &Alts, RuleMatcher &M,
+ InstructionMatcher &IM, const CodeGenInstructionPattern &P,
+ DenseSet<const Pattern *> &SeenPats, OperandDefLookupFn LookupOperandDef,
+ OperandMapperFnRef OperandMapper) {
+ auto StackTrace = PrettyStackTraceEmit(RuleDef, &P);
+
+ if (SeenPats.contains(&P))
+ return true;
+
+ SeenPats.insert(&P);
+
+ IM.addPredicate<InstructionOpcodeMatcher>(&P.getInst());
+ declareInstExpansion(CE, IM, P.getName());
+
+ // Check flags if needed.
+ if (const auto *FI = P.getMIFlagsInfo()) {
+ assert(FI->copy_flags().empty());
+
+ if (const auto &SetF = FI->set_flags(); !SetF.empty())
+ IM.addPredicate<MIFlagsInstructionPredicateMatcher>(SetF.getArrayRef());
+ if (const auto &UnsetF = FI->unset_flags(); !UnsetF.empty())
+ IM.addPredicate<MIFlagsInstructionPredicateMatcher>(UnsetF.getArrayRef(),
+ /*CheckNot=*/true);
+ }
+
+ 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.
+ //
+ // We use the remapped operand to emit immediates, and for the symbolic
+ // operand names (in IM.addOperand). CodeExpansions and OperandTable lookups
+ // still use the original name.
+ //
+ // The "def" flag on the remapped operand is always ignored.
+ auto RemappedO = OperandMapper(OriginalO);
+ assert(RemappedO.isNamedOperand() == OriginalO.isNamedOperand() &&
+ "Cannot remap an unnamed operand to a named one!");
+
+ const auto OpName =
+ RemappedO.isNamedOperand() ? RemappedO.getOperandName().str() : "";
+ OperandMatcher &OM =
+ IM.addOperand(Idx, OpName, AllocatedTemporariesBaseID++);
+ if (!OpName.empty())
+ declareOperandExpansion(CE, OM, OriginalO.getOperandName());
+
+ // Handle immediates.
+ if (RemappedO.hasImmValue()) {
+ if (isLiteralImm(P, Idx))
+ OM.addPredicate<LiteralIntOperandMatcher>(RemappedO.getImmValue());
+ else
+ OM.addPredicate<ConstantIntOperandMatcher>(RemappedO.getImmValue());
+ }
+
+ // Handle typed operands, but only bother to check if it hasn't been done
+ // before.
+ //
+ // getOperandMatcher will always return the first OM to have been created
+ // for that Operand. "OM" here is always a new OperandMatcher.
+ //
+ // Always emit a check for unnamed operands.
+ if (OpName.empty() ||
+ !M.getOperandMatcher(OpName).contains<LLTOperandMatcher>()) {
+ if (const auto Ty = RemappedO.getType()) {
+ // TODO: We could support GITypeOf here on the condition that the
+ // OperandMatcher exists already. Though it's clunky to make this work
+ // and isn't all that useful so it's just rejected in typecheckPatterns
+ // at this time.
+ assert(Ty.isLLT() && "Only LLTs are supported in match patterns!");
+ OM.addPredicate<LLTOperandMatcher>(getLLTCodeGen(Ty));
+ }
+ }
+
+ // Stop here if the operand is a def, or if it had no name.
+ if (OriginalO.isDef() || !OriginalO.isNamedOperand())
+ continue;
+
+ const auto *DefPat = LookupOperandDef(OriginalO.getOperandName());
+ if (!DefPat)
+ continue;
+
+ if (OriginalO.hasImmValue()) {
+ assert(!OpName.empty());
+ // This is a named immediate that also has a def, that's not okay.
+ // e.g.
+ // (G_SEXT $y, (i32 0))
+ // (COPY $x, 42:$y)
+ PrintError("'" + OpName +
+ "' is a named immediate, it cannot be defined by another "
+ "instruction");
+ PrintNote("'" + OpName + "' is defined by '" + DefPat->getName() + "'");
+ return false;
+ }
+
+ // From here we know that the operand defines an instruction, and we need to
+ // emit it.
+ auto InstOpM =
+ OM.addPredicate<InstructionOperandMatcher>(M, DefPat->getName());
+ 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 '" +
+ OriginalO.getOperandName() + "'");
+ return false;
+ }
+
+ auto &IM = (*InstOpM)->getInsnMatcher();
+ if (const auto *CGIDef = dyn_cast<CodeGenInstructionPattern>(DefPat)) {
+ if (!emitCodeGenInstructionMatchPattern(CE, Alts, M, IM, *CGIDef,
+ SeenPats, LookupOperandDef,
+ OperandMapper))
+ return false;
+ continue;
+ }
+
+ if (const auto *PFPDef = dyn_cast<PatFragPattern>(DefPat)) {
+ if (!emitPatFragMatchPattern(CE, Alts, M, &IM, *PFPDef, SeenPats))
+ return false;
+ continue;
+ }
+
+ llvm_unreachable("unknown type of InstructionPattern");
+ }
+
+ return true;
+}
+
+//===- GICombinerEmitter --------------------------------------------------===//
+
+/// Main implementation class. This emits the tablegenerated output.
+///
+/// It collects rules, uses `CombineRuleBuilder` to parse them and accumulate
+/// RuleMatchers, then takes all the necessary state/data from the various
+/// static storage pools and wires them together to emit the match table &
+/// associated function/data structures.
+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<std::pair<unsigned, std::string>> AllCombineRules;
+
+ // Keep track of all rules we've seen so far to ensure we don't process
+ // the same rule twice.
+ StringSet<> RulesSeen;
+
+ MatchTable buildMatchTable(MutableArrayRef<RuleMatcher> 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");
+ }
+
+ StringRef getCombineAllMethodName() const {
+ return Combiner->getValueAsString("CombineAllMethodName");
+ }
+
+ std::string getRuleConfigClassName() const {
+ return getClassName().str() + "RuleConfig";
+ }
+
+ void gatherRules(std::vector<RuleMatcher> &Rules,
+ const std::vector<Record *> &&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<std::pair<std::string, std::string>> Cases;
+ Cases.reserve(AllCombineRules.size());
+
+ for (const auto &[ID, Name] : AllCombineRules)
+ Cases.emplace_back(Name, "return " + to_string(ID) + ";\n");
+
+ OS << "static std::optional<uint64_t> 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<std::pair<uint64_t, uint64_t>> "
+ "getRuleRangeForIdentifier(StringRef RuleIdentifier) {\n"
+ << " std::pair<StringRef, StringRef> 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<std::string> " << Name << "Option;\n"
+ << "static cl::list<std::string> " << 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<std::string> " << 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() << "::" << getCombineAllMethodName()
+ << "(MachineInstr &I) const {\n"
+ << " const TargetSubtargetInfo &ST = MF.getSubtarget();\n"
+ << " const PredicateBitset AvailableFeatures = "
+ "getAvailableFeatures();\n"
+ << " B.setInstrAndDebugLoc(I);\n"
+ << " State.MIs.clear();\n"
+ << " State.MIs.push_back(&I);\n"
+ << " " << MatchDataInfo::StructName << " = "
+ << MatchDataInfo::StructTypeName << "();\n\n"
+ << " if (executeMatchTable(*this, State, ExecInfo, B"
+ << ", 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 = CXXPredicateCode::getAllMatchCode();
+ emitMIPredicateFnsImpl<const CXXPredicateCode *>(
+ OS, "", ArrayRef<const CXXPredicateCode *>(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<unsigned>(
+ OS, "I64", "int64_t", {}, [](unsigned) { return ""; },
+ [](unsigned) { return ""; });
+}
+
+void GICombinerEmitter::emitAPFloatImmPredicateFns(raw_ostream &OS) {
+ // Unused, but still needs to be called.
+ emitImmPredicateFnsImpl<unsigned>(
+ OS, "APFloat", "const APFloat &", {}, [](unsigned) { return ""; },
+ [](unsigned) { return ""; });
+}
+
+void GICombinerEmitter::emitAPIntImmPredicateFns(raw_ostream &OS) {
+ // Unused, but still needs to be called.
+ emitImmPredicateFnsImpl<unsigned>(
+ 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 = CXXPredicateCode::getAllApplyCode();
+
+ 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, "
+ "NewMIVector &OutMIs) const "
+ "{\n";
+ if (!ApplyCode.empty()) {
+ OS << " switch(ApplyID) {\n";
+ for (const auto &Apply : ApplyCode) {
+ OS << " case " << Apply->getEnumNameWithPrefix(CXXApplyPrefix) << ":{\n"
+ << " " << join(split(Apply->Code, '\n'), "\n ") << '\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<RuleMatcher> Rules) {
+ std::vector<Matcher *> InputRules;
+ for (Matcher &Rule : Rules)
+ InputRules.push_back(&Rule);
+
+ unsigned CurrentOrdering = 0;
+ StringMap<unsigned> 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<const RuleMatcher *>(A);
+ auto *R = static_cast<const RuleMatcher *>(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<std::unique_ptr<Matcher>> MatcherStorage;
+ std::vector<Matcher *> OptRules =
+ optimizeRules<GroupMatcher>(InputRules, MatcherStorage);
+
+ for (Matcher *Rule : OptRules)
+ Rule->optimize();
+
+ OptRules = optimizeRules<SwitchMatcher>(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<RuleMatcher> &ActiveRules,
+ const std::vector<Record *> &&RulesAndGroups) {
+ for (Record *Rec : RulesAndGroups) {
+ if (!Rec->isValueUnset("Rules")) {
+ gatherRules(ActiveRules, Rec->getValueAsListOfDefs("Rules"));
+ continue;
+ }
+
+ StringRef RuleName = Rec->getName();
+ if (!RulesSeen.insert(RuleName).second) {
+ PrintWarning(Rec->getLoc(),
+ "skipping rule '" + Rec->getName() +
+ "' because it has already been processed");
+ continue;
+ }
+
+ AllCombineRules.emplace_back(NextRuleID, Rec->getName().str());
+ CombineRuleBuilder CRB(Target, SubtargetFeatures, *Rec, NextRuleID++,
+ ActiveRules);
+
+ if (!CRB.parseAll()) {
+ assert(ErrorsPrinted && "Parsing failed without errors!");
+ continue;
+ }
+
+ if (StopAfterParse) {
+ CRB.print(outs());
+ continue;
+ }
+
+ if (!CRB.emitRuleMatchers()) {
+ assert(ErrorsPrinted && "Emission failed without errors!");
+ continue;
+ }
+ }
+}
+
+void GICombinerEmitter::run(raw_ostream &OS) {
+ InstructionOpcodeMatcher::initOpcodeValuesMap(Target);
+ LLTOperandMatcher::initTypeIDValuesMap();
+
+ Records.startTimer("Gather rules");
+ std::vector<RuleMatcher> Rules;
+ gatherRules(Rules, Combiner->getValueAsListOfDefs("Rules"));
+ if (ErrorsPrinted)
+ PrintFatalError(Combiner->getLoc(), "Failed to parse one or more rules");
+
+ if (StopAfterParse)
+ return;
+
+ Records.startTimer("Creating Match Table");
+ unsigned MaxTemporaries = 0;
+ for (const auto &Rule : Rules)
+ MaxTemporaries = std::max(MaxTemporaries, Rule.countRendererFns());
+
+ llvm::stable_sort(Rules, [&](const RuleMatcher &A, const RuleMatcher &B) {
+ if (A.isHigherPriorityThan(B)) {
+ assert(!B.isHigherPriorityThan(A) && "Cannot be more important "
+ "and less important at "
+ "the same time");
+ return true;
+ }
+ return false;
+ });
+
+ const MatchTable Table = buildMatchTable(Rules);
+
+ Records.startTimer("Emit combiner");
+
+ emitSourceFileHeader(getClassName().str() + " Combiner Match Table", OS);
+
+ // Unused
+ std::vector<StringRef> CustomRendererFns;
+ // Unused
+ std::vector<Record *> ComplexPredicates;
+
+ SmallVector<LLTCodeGen, 16> TypeObjects;
+ append_range(TypeObjects, KnownTypes);
+ llvm::sort(TypeObjects);
+
+ // Hack: Avoid empty declarator.
+ if (TypeObjects.empty())
+ TypeObjects.push_back(LLT::scalar(1));
+
+ // 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) {
+ EnablePrettyStackTrace();
+ 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", EmitGICombiner,
+ "Generate GlobalISel Combiner");