| //===- 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/CodeExpander.h" |
| #include "GlobalISel/CodeExpansions.h" |
| #include "GlobalISel/CombinerUtils.h" |
| #include "GlobalISelMatchTable.h" |
| #include "GlobalISelMatchTableExecutorEmitter.h" |
| #include "SubtargetFeatureInfo.h" |
| #include "llvm/ADT/APInt.h" |
| #include "llvm/ADT/Hashing.h" |
| #include "llvm/ADT/MapVector.h" |
| #include "llvm/ADT/Statistic.h" |
| #include "llvm/ADT/StringSet.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/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)); |
| |
| constexpr StringLiteral CXXApplyPrefix = "GICXXCustomAction_CombineApply"; |
| constexpr StringLiteral CXXPredPrefix = "GICXXPred_MI_Predicate_"; |
| constexpr StringLiteral PatFragClassName = "GICombinePatFrag"; |
| constexpr StringLiteral BuiltinInstClassName = "GIBuiltinInst"; |
| |
| std::string getIsEnabledPredicateEnumName(unsigned CombinerRuleID) { |
| return "GICXXPred_Simple_IsRule" + to_string(CombinerRuleID) + "Enabled"; |
| } |
| |
| /// Copies a StringRef into a static pool to make sure it has a static lifetime. |
| StringRef insertStrRef(StringRef S) { |
| if (S.empty()) |
| return {}; |
| |
| static StringSet<> Pool; |
| auto [It, Inserted] = Pool.insert(S); |
| return It->getKey(); |
| } |
| |
| 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) + "]"); |
| } |
| |
| std::string makeAnonPatName(StringRef Prefix, unsigned Idx) { |
| return ("__" + Prefix + "_" + Twine(Idx)).str(); |
| } |
| |
| 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; }); |
| } |
| |
| LLTCodeGen getLLTCodeGenFromRecord(const Record *Ty) { |
| assert(Ty->isSubClassOf("ValueType")); |
| return LLTCodeGen(*MVTToLLT(getValueType(Ty))); |
| } |
| |
| //===- MatchData Handling -------------------------------------------------===// |
| |
| /// Represents MatchData defined by the match stage and required by the apply |
| /// stage. |
| /// |
| /// This allows the plumbing of arbitrary data from C++ predicates between the |
| /// stages. |
| /// |
| /// When this class is initially created, it only has a pattern symbol and a |
| /// type. When all of the MatchDatas declarations of a given pattern have been |
| /// parsed, `AssignVariables` must be called to assign storage variable names to |
| /// each MatchDataInfo. |
| class MatchDataInfo { |
| StringRef PatternSymbol; |
| StringRef Type; |
| std::string VarName; |
| |
| public: |
| static constexpr StringLiteral StructTypeName = "MatchInfosTy"; |
| static constexpr StringLiteral StructName = "MatchInfos"; |
| |
| MatchDataInfo(StringRef PatternSymbol, StringRef Type) |
| : PatternSymbol(PatternSymbol), Type(Type.trim()) {} |
| |
| StringRef getPatternSymbol() const { return PatternSymbol; }; |
| StringRef getType() const { return Type; }; |
| |
| bool hasVariableName() const { return !VarName.empty(); } |
| void setVariableName(StringRef Name) { VarName = Name; } |
| StringRef getVariableName() const; |
| |
| std::string getQualifiedVariableName() const { |
| return StructName.str() + "." + getVariableName().str(); |
| } |
| |
| void print(raw_ostream &OS) const; |
| void dump() const { print(dbgs()); } |
| }; |
| |
| StringRef MatchDataInfo::getVariableName() const { |
| assert(hasVariableName()); |
| return VarName; |
| } |
| |
| void MatchDataInfo::print(raw_ostream &OS) const { |
| OS << "(MatchDataInfo pattern_symbol:" << PatternSymbol << " type:'" << Type |
| << "' var_name:" << (VarName.empty() ? "<unassigned>" : VarName) << ")"; |
| } |
| |
| /// Pool of type -> variables used to emit MatchData variables declarations. |
| /// |
| /// e.g. if the map contains "int64_t" -> ["MD0", "MD1"], then two variable |
| /// declarations must be emitted: `int64_t MD0` and `int64_t MD1`. |
| /// |
| /// This has a static lifetime and will outlive all the `MatchDataInfo` objects |
| /// by design. It needs to persist after all `CombineRuleBuilder` objects died |
| /// so we can emit the variable declarations. |
| StringMap<std::vector<std::string>> AllMatchDataVars; |
| |
| // Assign variable names to all MatchDatas used by a pattern. This must be |
| // called after all MatchData decls have been parsed inside a rule. |
| // |
| // Requires an array of MatchDataInfo so we can handle cases where a pattern |
| // uses multiple instances of the same MatchData type. |
| void AssignMatchDataVariables(MutableArrayRef<MatchDataInfo> Infos) { |
| static unsigned NextVarID = 0; |
| |
| StringMap<unsigned> SeenTypes; |
| for (auto &Info : Infos) { |
| unsigned &NumSeen = SeenTypes[Info.getType()]; |
| auto &ExistingVars = AllMatchDataVars[Info.getType()]; |
| |
| if (NumSeen == ExistingVars.size()) |
| ExistingVars.push_back("MDInfo" + to_string(NextVarID++)); |
| |
| Info.setVariableName(ExistingVars[NumSeen++]); |
| } |
| } |
| |
| //===- C++ Predicates Handling --------------------------------------------===// |
| |
| /// Entry into the static pool of all CXX Predicate code. This contains |
| /// fully expanded C++ code. |
| /// |
| /// The static pool is hidden inside the object and can be accessed through |
| /// getAllMatchCode/getAllApplyCode |
| /// |
| /// Note that CXXPattern trims C++ code, so the Code is already expected to be |
| /// free of leading/trailing whitespace. |
| class CXXPredicateCode { |
| using CXXPredicateCodePool = |
| DenseMap<hash_code, std::unique_ptr<CXXPredicateCode>>; |
| static CXXPredicateCodePool AllCXXMatchCode; |
| static CXXPredicateCodePool AllCXXApplyCode; |
| |
| /// Sorts a `CXXPredicateCodePool` by their IDs and returns it. |
| static std::vector<const CXXPredicateCode *> |
| getSorted(const CXXPredicateCodePool &Pool) { |
| std::vector<const CXXPredicateCode *> Out; |
| std::transform(Pool.begin(), Pool.end(), std::back_inserter(Out), |
| [&](auto &Elt) { return Elt.second.get(); }); |
| sort(Out, [](const auto *A, const auto *B) { return A->ID < B->ID; }); |
| return Out; |
| } |
| |
| /// Gets an instance of `CXXPredicateCode` for \p Code, or returns an already |
| /// existing one. |
| static const CXXPredicateCode &get(CXXPredicateCodePool &Pool, |
| std::string Code) { |
| // Check if we already have an identical piece of code, if not, create an |
| // entry in the pool. |
| const auto CodeHash = hash_value(Code); |
| if (auto It = Pool.find(CodeHash); It != Pool.end()) |
| return *It->second; |
| |
| const auto ID = Pool.size(); |
| auto OwnedData = std::unique_ptr<CXXPredicateCode>( |
| new CXXPredicateCode(std::move(Code), ID)); |
| const auto &DataRef = *OwnedData; |
| Pool[CodeHash] = std::move(OwnedData); |
| return DataRef; |
| } |
| |
| CXXPredicateCode(std::string Code, unsigned ID) |
| : Code(Code), ID(ID), BaseEnumName("GICombiner" + to_string(ID)) { |
| // Don't assert if ErrorsPrinted is set. This may mean CodeExpander failed, |
| // and it may add spaces in such cases. |
| assert((ErrorsPrinted || StringRef(Code).trim() == Code) && |
| "Code was expected to be trimmed!"); |
| } |
| |
| public: |
| static const CXXPredicateCode &getMatchCode(std::string Code) { |
| return get(AllCXXMatchCode, std::move(Code)); |
| } |
| |
| static const CXXPredicateCode &getApplyCode(std::string Code) { |
| return get(AllCXXApplyCode, std::move(Code)); |
| } |
| |
| static std::vector<const CXXPredicateCode *> getAllMatchCode() { |
| return getSorted(AllCXXMatchCode); |
| } |
| |
| static std::vector<const CXXPredicateCode *> getAllApplyCode() { |
| return getSorted(AllCXXApplyCode); |
| } |
| |
| const std::string Code; |
| const unsigned ID; |
| const std::string BaseEnumName; |
| |
| bool needsUnreachable() const { |
| return !StringRef(Code).starts_with("return"); |
| } |
| |
| std::string getEnumNameWithPrefix(StringRef Prefix) const { |
| return Prefix.str() + BaseEnumName; |
| } |
| }; |
| |
| CXXPredicateCode::CXXPredicateCodePool CXXPredicateCode::AllCXXMatchCode; |
| CXXPredicateCode::CXXPredicateCodePool CXXPredicateCode::AllCXXApplyCode; |
| |
| //===- Pattern Base Class -------------------------------------------------===// |
| |
| /// Base class for all patterns that can be written in an `apply`, `match` or |
| /// `pattern` DAG operator. |
| /// |
| /// For example: |
| /// |
| /// (apply (G_ZEXT $x, $y), (G_ZEXT $y, $z), "return isFoo(${z})") |
| /// |
| /// Creates 3 Pattern objects: |
| /// - Two CodeGenInstruction Patterns |
| /// - A CXXPattern |
| class Pattern { |
| public: |
| enum { |
| K_AnyOpcode, |
| K_CXX, |
| |
| K_CodeGenInstruction, |
| K_PatFrag, |
| K_Builtin, |
| }; |
| |
| virtual ~Pattern() = default; |
| |
| unsigned getKind() const { return Kind; } |
| const char *getKindName() const; |
| |
| bool hasName() const { return !Name.empty(); } |
| StringRef getName() const { return Name; } |
| |
| virtual void print(raw_ostream &OS, bool PrintName = true) const = 0; |
| void dump() const { return print(dbgs()); } |
| |
| protected: |
| Pattern(unsigned Kind, StringRef Name) |
| : Kind(Kind), Name(insertStrRef(Name)) { |
| assert(!Name.empty() && "unnamed pattern!"); |
| } |
| |
| void printImpl(raw_ostream &OS, bool PrintName, |
| function_ref<void()> ContentPrinter) const; |
| |
| private: |
| unsigned Kind; |
| StringRef Name; |
| }; |
| |
| const char *Pattern::getKindName() const { |
| switch (Kind) { |
| case K_AnyOpcode: |
| return "AnyOpcodePattern"; |
| case K_CXX: |
| return "CXXPattern"; |
| case K_CodeGenInstruction: |
| return "CodeGenInstructionPattern"; |
| case K_PatFrag: |
| return "PatFragPattern"; |
| case K_Builtin: |
| return "BuiltinPattern"; |
| } |
| |
| llvm_unreachable("unknown pattern kind!"); |
| } |
| |
| void Pattern::printImpl(raw_ostream &OS, bool PrintName, |
| function_ref<void()> ContentPrinter) const { |
| OS << "(" << getKindName() << " "; |
| if (PrintName) |
| OS << "name:" << getName() << " "; |
| ContentPrinter(); |
| OS << ")"; |
| } |
| |
| //===- AnyOpcodePattern ---------------------------------------------------===// |
| |
| /// `wip_match_opcode` patterns. |
| /// This matches one or more opcodes, and does not check any operands |
| /// whatsoever. |
| /// |
| /// TODO: Long-term, this needs to be removed. It's a hack around MIR |
| /// pattern matching limitations. |
| class AnyOpcodePattern : public Pattern { |
| public: |
| AnyOpcodePattern(StringRef Name) : Pattern(K_AnyOpcode, Name) {} |
| |
| static bool classof(const Pattern *P) { return P->getKind() == K_AnyOpcode; } |
| |
| void addOpcode(const CodeGenInstruction *I) { Insts.push_back(I); } |
| const auto &insts() const { return Insts; } |
| |
| void print(raw_ostream &OS, bool PrintName = true) const override; |
| |
| private: |
| SmallVector<const CodeGenInstruction *, 4> Insts; |
| }; |
| |
| void AnyOpcodePattern::print(raw_ostream &OS, bool PrintName) const { |
| printImpl(OS, PrintName, [&OS, this]() { |
| OS << "[" |
| << join(map_range(Insts, |
| [](const auto *I) { return I->TheDef->getName(); }), |
| ", ") |
| << "]"; |
| }); |
| } |
| |
| //===- CXXPattern ---------------------------------------------------------===// |
| |
| /// Represents raw C++ code which may need some expansions. |
| /// |
| /// e.g. [{ return isFooBux(${src}.getReg()); }] |
| /// |
| /// For the expanded code, \see CXXPredicateCode. CXXPredicateCode objects are |
| /// created through `expandCode`. |
| /// |
| /// \see CodeExpander and \see CodeExpansions for more information on code |
| /// expansions. |
| /// |
| /// This object has two purposes: |
| /// - Represent C++ code as a pattern entry. |
| /// - Be a factory for expanded C++ code. |
| /// - It's immutable and only holds the raw code so we can expand the same |
| /// CXX pattern multiple times if we need to. |
| /// |
| /// Note that the code is always trimmed in the constructor, so leading and |
| /// trailing whitespaces are removed. This removes bloat in the output, avoids |
| /// formatting issues, but also allows us to check things like |
| /// `.startswith("return")` trivially without worrying about spaces. |
| class CXXPattern : public Pattern { |
| public: |
| CXXPattern(const StringInit &Code, StringRef Name) |
| : CXXPattern(Code.getAsUnquotedString(), Name) {} |
| |
| CXXPattern(StringRef Code, StringRef Name) |
| : Pattern(K_CXX, Name), RawCode(Code.trim().str()) {} |
| |
| static bool classof(const Pattern *P) { return P->getKind() == K_CXX; } |
| |
| void setIsApply(bool Value = true) { IsApply = Value; } |
| StringRef getRawCode() const { return RawCode; } |
| |
| /// Expands raw code, replacing things such as `${foo}` with their |
| /// substitution in \p CE. |
| /// |
| /// \param CE Map of Code Expansions |
| /// \param Locs SMLocs for the Code Expander, in case it needs to emit |
| /// diagnostics. |
| /// \param AddComment If DebugCXXPreds is enabled, this is called to emit a |
| /// comment before the expanded code. |
| /// |
| /// \return A CXXPredicateCode object that contains the expanded code. Note |
| /// that this may or may not insert a new object. All CXXPredicateCode objects |
| /// are held in a set to avoid emitting duplicate C++ code. |
| const CXXPredicateCode & |
| expandCode(const CodeExpansions &CE, ArrayRef<SMLoc> Locs, |
| function_ref<void(raw_ostream &)> AddComment = {}) const; |
| |
| void print(raw_ostream &OS, bool PrintName = true) const override; |
| |
| private: |
| bool IsApply = false; |
| std::string RawCode; |
| }; |
| |
| const CXXPredicateCode & |
| CXXPattern::expandCode(const CodeExpansions &CE, ArrayRef<SMLoc> Locs, |
| function_ref<void(raw_ostream &)> AddComment) const { |
| std::string Result; |
| raw_string_ostream OS(Result); |
| |
| if (DebugCXXPreds && AddComment) |
| AddComment(OS); |
| |
| CodeExpander Expander(RawCode, CE, Locs, /*ShowExpansions*/ false); |
| Expander.emit(OS); |
| if (IsApply) |
| return CXXPredicateCode::getApplyCode(std::move(Result)); |
| return CXXPredicateCode::getMatchCode(std::move(Result)); |
| } |
| |
| void CXXPattern::print(raw_ostream &OS, bool PrintName) const { |
| printImpl(OS, PrintName, [&OS, this] { |
| OS << (IsApply ? "apply" : "match") << " code:\""; |
| printEscapedString(getRawCode(), OS); |
| OS << "\""; |
| }); |
| } |
| |
| //===- InstructionPattern ---------------------------------------------===// |
| |
| /// An operand for an InstructionPattern. |
| /// |
| /// Operands are composed of three elements: |
| /// - (Optional) Value |
| /// - (Optional) Name |
| /// - (Optional) Type |
| /// |
| /// Some examples: |
| /// (i32 0):$x -> V=int(0), Name='x', Type=i32 |
| /// 0:$x -> V=int(0), Name='x' |
| /// $x -> Name='x' |
| /// i32:$x -> Name='x', Type = i32 |
| class InstructionOperand { |
| public: |
| using IntImmTy = int64_t; |
| |
| InstructionOperand(IntImmTy Imm, StringRef Name, const Record *Type) |
| : Value(Imm), Name(insertStrRef(Name)), Type(Type) { |
| assert(!Type || Type->isSubClassOf("ValueType")); |
| } |
| |
| InstructionOperand(StringRef Name, const Record *Type) |
| : Name(insertStrRef(Name)), Type(Type) {} |
| |
| bool isNamedImmediate() const { return hasImmValue() && isNamedOperand(); } |
| |
| bool hasImmValue() const { return Value.has_value(); } |
| IntImmTy getImmValue() const { return *Value; } |
| |
| bool isNamedOperand() const { return !Name.empty(); } |
| StringRef getOperandName() const { |
| assert(isNamedOperand() && "Operand is unnamed"); |
| return Name; |
| } |
| |
| InstructionOperand withNewName(StringRef NewName) const { |
| InstructionOperand Result = *this; |
| Result.Name = insertStrRef(NewName); |
| return Result; |
| } |
| |
| void setIsDef(bool Value = true) { Def = Value; } |
| bool isDef() const { return Def; } |
| |
| void setType(const Record *R) { |
| assert((!Type || (Type == R)) && "Overwriting type!"); |
| Type = R; |
| } |
| const Record *getType() const { return Type; } |
| |
| std::string describe() const { |
| if (!hasImmValue()) |
| return "MachineOperand $" + getOperandName().str() + ""; |
| std::string Str = "imm " + to_string(getImmValue()); |
| if (isNamedImmediate()) |
| Str += ":$" + getOperandName().str() + ""; |
| return Str; |
| } |
| |
| void print(raw_ostream &OS) const { |
| if (isDef()) |
| OS << "<def>"; |
| |
| bool NeedsColon = true; |
| if (const Record *Ty = getType()) { |
| if (hasImmValue()) |
| OS << "(" << Ty->getName() << " " << getImmValue() << ")"; |
| else |
| OS << Ty->getName(); |
| } else if (hasImmValue()) |
| OS << getImmValue(); |
| else |
| NeedsColon = false; |
| |
| if (isNamedOperand()) |
| OS << (NeedsColon ? ":" : "") << "$" << getOperandName(); |
| } |
| |
| void dump() const { return print(dbgs()); } |
| |
| private: |
| std::optional<int64_t> Value; |
| StringRef Name; |
| const Record *Type = nullptr; |
| bool Def = false; |
| }; |
| |
| /// Base class for CodeGenInstructionPattern & PatFragPattern, which handles all |
| /// the boilerplate for patterns that have a list of operands for some (pseudo) |
| /// instruction. |
| class InstructionPattern : public Pattern { |
| public: |
| virtual ~InstructionPattern() = default; |
| |
| static bool classof(const Pattern *P) { |
| return P->getKind() == K_CodeGenInstruction || P->getKind() == K_PatFrag || |
| P->getKind() == K_Builtin; |
| } |
| |
| template <typename... Ty> void addOperand(Ty &&...Init) { |
| Operands.emplace_back(std::forward<Ty>(Init)...); |
| } |
| |
| auto &operands() { return Operands; } |
| const auto &operands() const { return Operands; } |
| unsigned operands_size() const { return Operands.size(); } |
| InstructionOperand &getOperand(unsigned K) { return Operands[K]; } |
| const InstructionOperand &getOperand(unsigned K) const { return Operands[K]; } |
| |
| /// When this InstructionPattern is used as the match root, returns the |
| /// operands that must be redefined in the 'apply' pattern for the rule to be |
| /// valid. |
| /// |
| /// For most patterns, this just returns the defs. |
| /// For PatFrag this only returns the root of the PF. |
| /// |
| /// Returns an empty array on error. |
| virtual ArrayRef<InstructionOperand> getApplyDefsNeeded() const { |
| return {operands().begin(), getNumInstDefs()}; |
| } |
| |
| auto named_operands() { |
| return make_filter_range(Operands, |
| [&](auto &O) { return O.isNamedOperand(); }); |
| } |
| |
| auto named_operands() const { |
| return make_filter_range(Operands, |
| [&](auto &O) { return O.isNamedOperand(); }); |
| } |
| |
| virtual bool isVariadic() const { return false; } |
| virtual unsigned getNumInstOperands() const = 0; |
| virtual unsigned getNumInstDefs() const = 0; |
| |
| bool hasAllDefs() const { return operands_size() >= getNumInstDefs(); } |
| |
| virtual StringRef getInstName() const = 0; |
| |
| void reportUnreachable(ArrayRef<SMLoc> Locs) const; |
| virtual bool checkSemantics(ArrayRef<SMLoc> Loc); |
| |
| void print(raw_ostream &OS, bool PrintName = true) const override; |
| |
| protected: |
| InstructionPattern(unsigned K, StringRef Name) : Pattern(K, Name) {} |
| |
| SmallVector<InstructionOperand, 4> Operands; |
| }; |
| |
| void InstructionPattern::reportUnreachable(ArrayRef<SMLoc> Locs) const { |
| PrintError(Locs, "pattern '" + getName() + "' ('" + getInstName() + |
| "') is unreachable from the pattern root!"); |
| } |
| |
| bool InstructionPattern::checkSemantics(ArrayRef<SMLoc> Loc) { |
| unsigned NumExpectedOperands = getNumInstOperands(); |
| |
| if (isVariadic()) { |
| if (Operands.size() < NumExpectedOperands) { |
| PrintError(Loc, +"'" + getInstName() + "' expected at least " + |
| Twine(NumExpectedOperands) + " operands, got " + |
| Twine(Operands.size())); |
| return false; |
| } |
| } else if (NumExpectedOperands != Operands.size()) { |
| PrintError(Loc, +"'" + getInstName() + "' expected " + |
| Twine(NumExpectedOperands) + " operands, got " + |
| Twine(Operands.size())); |
| return false; |
| } |
| |
| unsigned OpIdx = 0; |
| unsigned NumDefs = getNumInstDefs(); |
| for (auto &Op : Operands) |
| Op.setIsDef(OpIdx++ < NumDefs); |
| |
| return true; |
| } |
| |
| void InstructionPattern::print(raw_ostream &OS, bool PrintName) const { |
| printImpl(OS, PrintName, [&OS, this] { |
| OS << getInstName() << " operands:["; |
| StringRef Sep = ""; |
| for (const auto &Op : Operands) { |
| OS << Sep; |
| Op.print(OS); |
| Sep = ", "; |
| } |
| OS << "]"; |
| }); |
| } |
| |
| //===- OperandTable -------------------------------------------------------===// |
| |
| /// Maps InstructionPattern operands to their definitions. This allows us to tie |
| /// different patterns of a (apply), (match) or (patterns) set of patterns |
| /// together. |
| template <typename DefTy = InstructionPattern> class OperandTable { |
| public: |
| static_assert(std::is_base_of_v<InstructionPattern, DefTy>, |
| "DefTy should be a derived class from InstructionPattern"); |
| |
| bool addPattern(DefTy *P, function_ref<void(StringRef)> DiagnoseRedef) { |
| for (const auto &Op : P->named_operands()) { |
| StringRef OpName = Op.getOperandName(); |
| |
| // We always create an entry in the OperandTable, even for uses. |
| // Uses of operands that don't have a def (= live-ins) will remain with a |
| // nullptr as the Def. |
| // |
| // This allows us tell whether an operand exists in a pattern or not. If |
| // there is no entry for it, it doesn't exist, if there is an entry, it's |
| // used/def'd at least once. |
| auto &Def = Table[OpName]; |
| |
| if (!Op.isDef()) |
| continue; |
| |
| if (Def) { |
| DiagnoseRedef(OpName); |
| return false; |
| } |
| |
| Def = P; |
| } |
| |
| return true; |
| } |
| |
| struct LookupResult { |
| LookupResult() = default; |
| LookupResult(DefTy *Def) : Found(true), Def(Def) {} |
| |
| bool Found = false; |
| DefTy *Def = nullptr; |
| |
| bool isLiveIn() const { return Found && !Def; } |
| }; |
| |
| LookupResult lookup(StringRef OpName) const { |
| if (auto It = Table.find(OpName); It != Table.end()) |
| return LookupResult(It->second); |
| return LookupResult(); |
| } |
| |
| DefTy *getDef(StringRef OpName) const { return lookup(OpName).Def; } |
| |
| void print(raw_ostream &OS, StringRef Name = "", |
| StringRef Indent = "") const { |
| OS << Indent << "(OperandTable "; |
| if (!Name.empty()) |
| OS << Name << " "; |
| if (Table.empty()) { |
| OS << "<empty>)\n"; |
| return; |
| } |
| |
| SmallVector<StringRef, 0> Keys(Table.keys()); |
| sort(Keys); |
| |
| OS << "\n"; |
| for (const auto &Key : Keys) { |
| const auto *Def = Table.at(Key); |
| OS << Indent << " " << Key << " -> " |
| << (Def ? Def->getName() : "<live-in>") << "\n"; |
| } |
| OS << Indent << ")\n"; |
| } |
| |
| auto begin() const { return Table.begin(); } |
| auto end() const { return Table.end(); } |
| |
| void dump() const { print(dbgs()); } |
| |
| private: |
| StringMap<DefTy *> Table; |
| }; |
| |
| //===- CodeGenInstructionPattern ------------------------------------------===// |
| |
| /// Matches an instruction, e.g. `G_ADD $x, $y, $z`. |
| class CodeGenInstructionPattern : public InstructionPattern { |
| public: |
| CodeGenInstructionPattern(const CodeGenInstruction &I, StringRef Name) |
| : InstructionPattern(K_CodeGenInstruction, Name), I(I) {} |
| |
| static bool classof(const Pattern *P) { |
| return P->getKind() == K_CodeGenInstruction; |
| } |
| |
| bool is(StringRef OpcodeName) const { |
| return I.TheDef->getName() == OpcodeName; |
| } |
| |
| bool hasVariadicDefs() const; |
| bool isVariadic() const override { return I.Operands.isVariadic; } |
| unsigned getNumInstDefs() const override; |
| unsigned getNumInstOperands() const override; |
| |
| const CodeGenInstruction &getInst() const { return I; } |
| StringRef getInstName() const override { return I.TheDef->getName(); } |
| |
| private: |
| const CodeGenInstruction &I; |
| }; |
| |
| bool CodeGenInstructionPattern::hasVariadicDefs() const { |
| // Note: we cannot use variadicOpsAreDefs, it's not set for |
| // GenericInstructions. |
| if (!isVariadic()) |
| return false; |
| |
| if (I.variadicOpsAreDefs) |
| return true; |
| |
| DagInit *OutOps = I.TheDef->getValueAsDag("OutOperandList"); |
| if (OutOps->arg_empty()) |
| return false; |
| |
| auto *LastArgTy = dyn_cast<DefInit>(OutOps->getArg(OutOps->arg_size() - 1)); |
| return LastArgTy && LastArgTy->getDef()->getName() == "variable_ops"; |
| } |
| |
| unsigned CodeGenInstructionPattern::getNumInstDefs() const { |
| if (!isVariadic() || !hasVariadicDefs()) |
| return I.Operands.NumDefs; |
| unsigned NumOuts = I.Operands.size() - I.Operands.NumDefs; |
| assert(Operands.size() > NumOuts); |
| return std::max<unsigned>(I.Operands.NumDefs, Operands.size() - NumOuts); |
| } |
| |
| unsigned CodeGenInstructionPattern::getNumInstOperands() const { |
| unsigned NumCGIOps = I.Operands.size(); |
| return isVariadic() ? std::max<unsigned>(NumCGIOps, Operands.size()) |
| : NumCGIOps; |
| } |
| |
| //===- OperandTypeChecker -------------------------------------------------===// |
| |
| /// This is a trivial type checker for all operands in a set of |
| /// InstructionPatterns. |
| /// |
| /// It infers the type of each operand, check it's consistent with the known |
| /// type of the operand, and then sets all of the types in all operands in |
| /// setAllOperandTypes. |
| class OperandTypeChecker { |
| public: |
| OperandTypeChecker(ArrayRef<SMLoc> DiagLoc) : DiagLoc(DiagLoc) {} |
| |
| bool check(InstructionPattern *P); |
| |
| void setAllOperandTypes(); |
| |
| private: |
| struct OpTypeInfo { |
| const Record *Type = nullptr; |
| InstructionPattern *TypeSrc = nullptr; |
| }; |
| |
| ArrayRef<SMLoc> DiagLoc; |
| StringMap<OpTypeInfo> Types; |
| |
| SmallVector<InstructionPattern *, 16> Pats; |
| }; |
| |
| bool OperandTypeChecker::check(InstructionPattern *P) { |
| Pats.push_back(P); |
| |
| for (auto &Op : P->named_operands()) { |
| const Record *Ty = Op.getType(); |
| if (!Ty) |
| continue; |
| |
| auto &Info = Types[Op.getOperandName()]; |
| |
| if (!Info.Type) { |
| Info.Type = Ty; |
| Info.TypeSrc = P; |
| continue; |
| } |
| |
| if (Info.Type != Ty) { |
| PrintError(DiagLoc, "conflicting types for operand '" + |
| Op.getOperandName() + "': first seen with '" + |
| Info.Type->getName() + "' in '" + |
| Info.TypeSrc->getName() + ", now seen with '" + |
| Ty->getName() + "' in '" + P->getName() + "'"); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| void OperandTypeChecker::setAllOperandTypes() { |
| for (auto *Pat : Pats) { |
| for (auto &Op : Pat->named_operands()) { |
| if (auto &Info = Types[Op.getOperandName()]; Info.Type) |
| Op.setType(Info.Type); |
| } |
| } |
| } |
| |
| //===- PatFrag ------------------------------------------------------------===// |
| |
| /// Represents a parsed GICombinePatFrag. This can be thought of as the |
| /// equivalent of a CodeGenInstruction, but for PatFragPatterns. |
| /// |
| /// PatFrags are made of 3 things: |
| /// - Out parameters (defs) |
| /// - In parameters |
| /// - A set of pattern lists (alternatives). |
| /// |
| /// If the PatFrag uses instruction patterns, the root must be one of the defs. |
| /// |
| /// Note that this DOES NOT represent the use of the PatFrag, only its |
| /// definition. The use of the PatFrag in a Pattern is represented by |
| /// PatFragPattern. |
| /// |
| /// PatFrags use the term "parameter" instead of operand because they're |
| /// essentially macros, and using that name avoids confusion. Other than that, |
| /// they're structured similarly to a MachineInstruction - all parameters |
| /// (operands) are in the same list, with defs at the start. This helps mapping |
| /// parameters to values, because, param N of a PatFrag is always operand N of a |
| /// PatFragPattern. |
| class PatFrag { |
| public: |
| enum ParamKind { |
| PK_Root, |
| PK_MachineOperand, |
| PK_Imm, |
| }; |
| |
| struct Param { |
| StringRef Name; |
| ParamKind Kind; |
| }; |
| |
| using ParamVec = SmallVector<Param, 4>; |
| using ParamIt = ParamVec::const_iterator; |
| |
| /// Represents an alternative of the PatFrag. When parsing a GICombinePatFrag, |
| /// this is created from its "Alternatives" list. Each alternative is a list |
| /// of patterns written wrapped in a `(pattern ...)` dag init. |
| /// |
| /// Each argument to the `pattern` DAG operator is parsed into a Pattern |
| /// instance. |
| struct Alternative { |
| OperandTable<> OpTable; |
| SmallVector<std::unique_ptr<Pattern>, 4> Pats; |
| }; |
| |
| explicit PatFrag(const Record &Def) : Def(Def) { |
| assert(Def.isSubClassOf(PatFragClassName)); |
| } |
| |
| static StringRef getParamKindStr(ParamKind OK); |
| |
| StringRef getName() const { return Def.getName(); } |
| |
| const Record &getDef() const { return Def; } |
| ArrayRef<SMLoc> getLoc() const { return Def.getLoc(); } |
| |
| Alternative &addAlternative() { return Alts.emplace_back(); } |
| const Alternative &getAlternative(unsigned K) const { return Alts[K]; } |
| unsigned num_alternatives() const { return Alts.size(); } |
| |
| void addInParam(StringRef Name, ParamKind Kind); |
| iterator_range<ParamIt> in_params() const; |
| unsigned num_in_params() const { return Params.size() - NumOutParams; } |
| |
| void addOutParam(StringRef Name, ParamKind Kind); |
| iterator_range<ParamIt> out_params() const; |
| unsigned num_out_params() const { return NumOutParams; } |
| |
| unsigned num_roots() const; |
| unsigned num_params() const { return num_in_params() + num_out_params(); } |
| |
| /// Finds the operand \p Name and returns its index or -1 if not found. |
| /// Remember that all params are part of the same list, with out params at the |
| /// start. This means that the index returned can be used to access operands |
| /// of InstructionPatterns. |
| unsigned getParamIdx(StringRef Name) const; |
| const Param &getParam(unsigned K) const { return Params[K]; } |
| |
| bool canBeMatchRoot() const { return num_roots() == 1; } |
| |
| void print(raw_ostream &OS, StringRef Indent = "") const; |
| void dump() const { print(dbgs()); } |
| |
| /// Checks if the in-param \p ParamName can be unbound or not. |
| /// \p ArgName is the name of the argument passed to the PatFrag. |
| /// |
| /// An argument can be unbound only if, for all alternatives: |
| /// - There is no CXX pattern, OR: |
| /// - There is an InstructionPattern that binds the parameter. |
| /// |
| /// e.g. in (MyPatFrag $foo), if $foo has never been seen before (= it's |
| /// unbound), this checks if MyPatFrag supports it or not. |
| bool handleUnboundInParam(StringRef ParamName, StringRef ArgName, |
| ArrayRef<SMLoc> DiagLoc) const; |
| |
| bool checkSemantics(); |
| bool buildOperandsTables(); |
| |
| private: |
| static void printParamsList(raw_ostream &OS, iterator_range<ParamIt> Params); |
| |
| void PrintError(Twine Msg) const { ::PrintError(&Def, Msg); } |
| |
| const Record &Def; |
| unsigned NumOutParams = 0; |
| ParamVec Params; |
| SmallVector<Alternative, 2> Alts; |
| }; |
| |
| StringRef PatFrag::getParamKindStr(ParamKind OK) { |
| switch (OK) { |
| case PK_Root: |
| return "root"; |
| case PK_MachineOperand: |
| return "machine_operand"; |
| case PK_Imm: |
| return "imm"; |
| } |
| |
| llvm_unreachable("Unknown operand kind!"); |
| } |
| |
| void PatFrag::addInParam(StringRef Name, ParamKind Kind) { |
| Params.emplace_back(Param{insertStrRef(Name), Kind}); |
| } |
| |
| iterator_range<PatFrag::ParamIt> PatFrag::in_params() const { |
| return {Params.begin() + NumOutParams, Params.end()}; |
| } |
| |
| void PatFrag::addOutParam(StringRef Name, ParamKind Kind) { |
| assert(NumOutParams == Params.size() && |
| "Adding out-param after an in-param!"); |
| Params.emplace_back(Param{insertStrRef(Name), Kind}); |
| ++NumOutParams; |
| } |
| |
| iterator_range<PatFrag::ParamIt> PatFrag::out_params() const { |
| return {Params.begin(), Params.begin() + NumOutParams}; |
| } |
| |
| unsigned PatFrag::num_roots() const { |
| return count_if(out_params(), |
| [&](const auto &P) { return P.Kind == PK_Root; }); |
| } |
| |
| unsigned PatFrag::getParamIdx(StringRef Name) const { |
| for (const auto &[Idx, Op] : enumerate(Params)) { |
| if (Op.Name == Name) |
| return Idx; |
| } |
| |
| return -1; |
| } |
| |
| bool PatFrag::checkSemantics() { |
| for (const auto &Alt : Alts) { |
| for (const auto &Pat : Alt.Pats) { |
| switch (Pat->getKind()) { |
| case Pattern::K_AnyOpcode: |
| PrintError("wip_match_opcode cannot be used in " + PatFragClassName); |
| return false; |
| case Pattern::K_Builtin: |
| PrintError("Builtin instructions cannot be used in " + |
| PatFragClassName); |
| return false; |
| case Pattern::K_CXX: |
| case Pattern::K_CodeGenInstruction: |
| continue; |
| case Pattern::K_PatFrag: |
| // TODO: It's just that the emitter doesn't handle it but technically |
| // there is no reason why we can't. We just have to be careful with |
| // operand mappings, it could get complex. |
| PrintError("nested " + PatFragClassName + " are not supported"); |
| return false; |
| } |
| } |
| } |
| |
| StringSet<> SeenOps; |
| for (const auto &Op : in_params()) { |
| if (SeenOps.count(Op.Name)) { |
| PrintError("duplicate parameter '" + Op.Name + "'"); |
| return false; |
| } |
| |
| // Check this operand is NOT defined in any alternative's patterns. |
| for (const auto &Alt : Alts) { |
| if (Alt.OpTable.lookup(Op.Name).Def) { |
| PrintError("input parameter '" + Op.Name + "' cannot be redefined!"); |
| return false; |
| } |
| } |
| |
| if (Op.Kind == PK_Root) { |
| PrintError("input parameterr '" + Op.Name + "' cannot be a root!"); |
| return false; |
| } |
| |
| SeenOps.insert(Op.Name); |
| } |
| |
| for (const auto &Op : out_params()) { |
| if (Op.Kind != PK_Root && Op.Kind != PK_MachineOperand) { |
| PrintError("output parameter '" + Op.Name + |
| "' must be 'root' or 'gi_mo'"); |
| return false; |
| } |
| |
| if (SeenOps.count(Op.Name)) { |
| PrintError("duplicate parameter '" + Op.Name + "'"); |
| return false; |
| } |
| |
| // Check this operand is defined in all alternative's patterns. |
| for (const auto &Alt : Alts) { |
| const auto *OpDef = Alt.OpTable.getDef(Op.Name); |
| if (!OpDef) { |
| PrintError("output parameter '" + Op.Name + |
| "' must be defined by all alternative patterns in '" + |
| Def.getName() + "'"); |
| return false; |
| } |
| |
| if (Op.Kind == PK_Root && OpDef->getNumInstDefs() != 1) { |
| // The instruction that defines the root must have a single def. |
| // Otherwise we'd need to support multiple roots and it gets messy. |
| // |
| // e.g. this is not supported: |
| // (pattern (G_UNMERGE_VALUES $x, $root, $vec)) |
| PrintError("all instructions that define root '" + Op.Name + "' in '" + |
| Def.getName() + "' can only have a single output operand"); |
| return false; |
| } |
| } |
| |
| SeenOps.insert(Op.Name); |
| } |
| |
| if (num_out_params() != 0 && num_roots() == 0) { |
| PrintError(PatFragClassName + " must have one root in its 'out' operands"); |
| return false; |
| } |
| |
| if (num_roots() > 1) { |
| PrintError(PatFragClassName + " can only have one root"); |
| return false; |
| } |
| |
| // TODO: find unused params |
| |
| // Now, typecheck all alternatives. |
| for (auto &Alt : Alts) { |
| OperandTypeChecker OTC(Def.getLoc()); |
| for (auto &Pat : Alt.Pats) { |
| if (auto *IP = dyn_cast<InstructionPattern>(Pat.get())) { |
| if (!OTC.check(IP)) |
| return false; |
| } |
| } |
| OTC.setAllOperandTypes(); |
| } |
| |
| return true; |
| } |
| |
| bool PatFrag::handleUnboundInParam(StringRef ParamName, StringRef ArgName, |
| ArrayRef<SMLoc> DiagLoc) const { |
| // The parameter must be a live-in of all alternatives for this to work. |
| // Otherwise, we risk having unbound parameters being used (= crashes). |
| // |
| // Examples: |
| // |
| // in (ins $y), (patterns (G_FNEG $dst, $y), "return matchFnegOp(${y})") |
| // even if $y is unbound, we'll lazily bind it when emitting the G_FNEG. |
| // |
| // in (ins $y), (patterns "return matchFnegOp(${y})") |
| // if $y is unbound when this fragment is emitted, C++ code expansion will |
| // fail. |
| for (const auto &Alt : Alts) { |
| auto &OT = Alt.OpTable; |
| if (!OT.lookup(ParamName).Found) { |
| ::PrintError(DiagLoc, "operand '" + ArgName + "' (for parameter '" + |
| ParamName + "' of '" + getName() + |
| "') cannot be unbound"); |
| PrintNote( |
| DiagLoc, |
| "one or more alternatives of '" + getName() + "' do not bind '" + |
| ParamName + |
| "' to an instruction operand; either use a bound operand or " |
| "ensure '" + |
| Def.getName() + "' binds '" + ParamName + |
| "' in all alternatives"); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool PatFrag::buildOperandsTables() { |
| // enumerate(...) doesn't seem to allow lvalues so we need to count the old |
| // way. |
| unsigned Idx = 0; |
| |
| const auto DiagnoseRedef = [this, &Idx](StringRef OpName) { |
| PrintError("Operand '" + OpName + |
| "' is defined multiple times in patterns of alternative #" + |
| to_string(Idx)); |
| }; |
| |
| for (auto &Alt : Alts) { |
| for (auto &Pat : Alt.Pats) { |
| auto *IP = dyn_cast<InstructionPattern>(Pat.get()); |
| if (!IP) |
| continue; |
| |
| if (!Alt.OpTable.addPattern(IP, DiagnoseRedef)) |
| return false; |
| } |
| |
| ++Idx; |
| } |
| |
| return true; |
| } |
| |
| void PatFrag::print(raw_ostream &OS, StringRef Indent) const { |
| OS << Indent << "(PatFrag name:" << getName() << "\n"; |
| if (!in_params().empty()) { |
| OS << Indent << " (ins "; |
| printParamsList(OS, in_params()); |
| OS << ")\n"; |
| } |
| |
| if (!out_params().empty()) { |
| OS << Indent << " (outs "; |
| printParamsList(OS, out_params()); |
| OS << ")\n"; |
| } |
| |
| // TODO: Dump OperandTable as well. |
| OS << Indent << " (alternatives [\n"; |
| for (const auto &Alt : Alts) { |
| OS << Indent << " [\n"; |
| for (const auto &Pat : Alt.Pats) { |
| OS << Indent << " "; |
| Pat->print(OS, /*PrintName=*/true); |
| OS << ",\n"; |
| } |
| OS << Indent << " ],\n"; |
| } |
| OS << Indent << " ])\n"; |
| |
| OS << Indent << ')'; |
| } |
| |
| void PatFrag::printParamsList(raw_ostream &OS, iterator_range<ParamIt> Params) { |
| OS << '[' |
| << join(map_range(Params, |
| [](auto &O) { |
| return (O.Name + ":" + getParamKindStr(O.Kind)).str(); |
| }), |
| ", ") |
| << ']'; |
| } |
| |
| //===- PatFragPattern -----------------------------------------------------===// |
| |
| class PatFragPattern : public InstructionPattern { |
| public: |
| PatFragPattern(const PatFrag &PF, StringRef Name) |
| : InstructionPattern(K_PatFrag, Name), PF(PF) {} |
| |
| static bool classof(const Pattern *P) { return P->getKind() == K_PatFrag; } |
| |
| const PatFrag &getPatFrag() const { return PF; } |
| StringRef getInstName() const override { return PF.getName(); } |
| |
| unsigned getNumInstDefs() const override { return PF.num_out_params(); } |
| unsigned getNumInstOperands() const override { return PF.num_params(); } |
| |
| ArrayRef<InstructionOperand> getApplyDefsNeeded() const override; |
| |
| bool checkSemantics(ArrayRef<SMLoc> DiagLoc) override; |
| |
| /// Before emitting the patterns inside the PatFrag, add all necessary code |
| /// expansions to \p PatFragCEs imported from \p ParentCEs. |
| /// |
| /// For a MachineOperand PatFrag parameter, this will fetch the expansion for |
| /// that operand from \p ParentCEs and add it to \p PatFragCEs. Errors can be |
| /// emitted if the MachineOperand reference is unbound. |
| /// |
| /// For an Immediate PatFrag parameter this simply adds the integer value to |
| /// \p PatFragCEs as an expansion. |
| /// |
| /// \param ParentCEs Contains all of the code expansions declared by the other |
| /// patterns emitted so far in the pattern list containing |
| /// this PatFragPattern. |
| /// \param PatFragCEs Output Code Expansions (usually empty) |
| /// \param DiagLoc Diagnostic loc in case an error occurs. |
| /// \return `true` on success, `false` on failure. |
| bool mapInputCodeExpansions(const CodeExpansions &ParentCEs, |
| CodeExpansions &PatFragCEs, |
| ArrayRef<SMLoc> DiagLoc) const; |
| |
| private: |
| const PatFrag &PF; |
| }; |
| |
| ArrayRef<InstructionOperand> PatFragPattern::getApplyDefsNeeded() const { |
| assert(PF.num_roots() == 1); |
| // Only roots need to be redef. |
| for (auto [Idx, Param] : enumerate(PF.out_params())) { |
| if (Param.Kind == PatFrag::PK_Root) |
| return getOperand(Idx); |
| } |
| llvm_unreachable("root not found!"); |
| } |
| |
| bool PatFragPattern::checkSemantics(ArrayRef<SMLoc> DiagLoc) { |
| if (!InstructionPattern::checkSemantics(DiagLoc)) |
| return false; |
| |
| for (const auto &[Idx, Op] : enumerate(Operands)) { |
| switch (PF.getParam(Idx).Kind) { |
| case PatFrag::PK_Imm: |
| if (!Op.hasImmValue()) { |
| PrintError(DiagLoc, "expected operand " + to_string(Idx) + " of '" + |
| getInstName() + "' to be an immediate; got " + |
| Op.describe()); |
| return false; |
| } |
| if (Op.isNamedImmediate()) { |
| PrintError(DiagLoc, "operand " + to_string(Idx) + " of '" + |
| getInstName() + |
| "' cannot be a named immediate"); |
| return false; |
| } |
| break; |
| case PatFrag::PK_Root: |
| case PatFrag::PK_MachineOperand: |
| if (!Op.isNamedOperand() || Op.isNamedImmediate()) { |
| PrintError(DiagLoc, "expected operand " + to_string(Idx) + " of '" + |
| getInstName() + |
| "' to be a MachineOperand; got " + |
| Op.describe()); |
| return false; |
| } |
| break; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool PatFragPattern::mapInputCodeExpansions(const CodeExpansions &ParentCEs, |
| CodeExpansions &PatFragCEs, |
| ArrayRef<SMLoc> DiagLoc) const { |
| for (const auto &[Idx, Op] : enumerate(operands())) { |
| StringRef ParamName = PF.getParam(Idx).Name; |
| |
| // Operands to a PFP can only be named, or be an immediate, but not a named |
| // immediate. |
| assert(!Op.isNamedImmediate()); |
| |
| if (Op.isNamedOperand()) { |
| StringRef ArgName = Op.getOperandName(); |
| // Map it only if it's been defined. |
| auto It = ParentCEs.find(ArgName); |
| if (It == ParentCEs.end()) { |
| if (!PF.handleUnboundInParam(ParamName, ArgName, DiagLoc)) |
| return false; |
| } else |
| PatFragCEs.declare(ParamName, It->second); |
| continue; |
| } |
| |
| if (Op.hasImmValue()) { |
| PatFragCEs.declare(ParamName, to_string(Op.getImmValue())); |
| continue; |
| } |
| |
| llvm_unreachable("Unknown Operand Type!"); |
| } |
| |
| return true; |
| } |
| |
| //===- BuiltinPattern -----------------------------------------------------===// |
| |
| enum BuiltinKind { |
| BI_ReplaceReg, |
| BI_EraseRoot, |
| }; |
| |
| class BuiltinPattern : public InstructionPattern { |
| struct BuiltinInfo { |
| StringLiteral DefName; |
| BuiltinKind Kind; |
| unsigned NumOps; |
| unsigned NumDefs; |
| }; |
| |
| static constexpr std::array<BuiltinInfo, 2> KnownBuiltins = {{ |
| {"GIReplaceReg", BI_ReplaceReg, 2, 1}, |
| {"GIEraseRoot", BI_EraseRoot, 0, 0}, |
| }}; |
| |
| public: |
| BuiltinPattern(const Record &Def, StringRef Name) |
| : InstructionPattern(K_Builtin, Name), I(getBuiltinInfo(Def)) {} |
| |
| static bool classof(const Pattern *P) { return P->getKind() == K_Builtin; } |
| |
| unsigned getNumInstOperands() const override { return I.NumOps; } |
| unsigned getNumInstDefs() const override { return I.NumDefs; } |
| StringRef getInstName() const override { return I.DefName; } |
| BuiltinKind getBuiltinKind() const { return I.Kind; } |
| |
| bool checkSemantics(ArrayRef<SMLoc> Loc) override; |
| |
| private: |
| static BuiltinInfo getBuiltinInfo(const Record &Def); |
| |
| BuiltinInfo I; |
| }; |
| |
| BuiltinPattern::BuiltinInfo BuiltinPattern::getBuiltinInfo(const Record &Def) { |
| assert(Def.isSubClassOf(BuiltinInstClassName)); |
| |
| StringRef Name = Def.getName(); |
| for (const auto &KBI : KnownBuiltins) { |
| if (KBI.DefName == Name) |
| return KBI; |
| } |
| |
| PrintFatalError(Def.getLoc(), "Unimplemented " + BuiltinInstClassName + |
| " def '" + Name + "'"); |
| } |
| |
| bool BuiltinPattern::checkSemantics(ArrayRef<SMLoc> Loc) { |
| if (!InstructionPattern::checkSemantics(Loc)) |
| return false; |
| |
| // For now all builtins just take names, no immediates. |
| for (const auto &[Idx, Op] : enumerate(operands())) { |
| if (!Op.isNamedOperand() || Op.isNamedImmediate()) { |
| PrintError(Loc, "expected operand " + to_string(Idx) + " of '" + |
| getInstName() + "' to be a name"); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| //===- 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(PatFragClassName)) |
| OS << "Parsing " << PatFragClassName << " '" << 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(PatFragClassName)) |
| OS << "Emitting " << PatFragClassName << " '" << Def.getName() << "'"; |
| else |
| OS << "Emitting '" << Def.getName() << "'"; |
| |
| if (Pat) |
| OS << " [" << Pat->getKindName() << " '" << Pat->getName() << "']"; |
| OS << "\n"; |
| } |
| }; |
| |
| //===- 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; |
| 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 " + PatFragClassName + |
| " 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()); |
| const auto &ExpandedCode = |
| P.expandCode(CE, RuleDef.getLoc(), [&](raw_ostream &OS) { |
| OS << "// Pattern Alternatives: "; |
| print(OS, Alts); |
| OS << "\n"; |
| }); |
| 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() { |
| OperandTypeChecker OTC(RuleDef.getLoc()); |
| |
| for (auto &Pat : values(MatchPats)) { |
| if (auto *IP = dyn_cast<InstructionPattern>(Pat.get())) { |
| if (!OTC.check(IP)) |
| return false; |
| } |
| } |
| |
| for (auto &Pat : values(ApplyPats)) { |
| if (auto *IP = dyn_cast<InstructionPattern>(Pat.get())) { |
| if (!OTC.check(IP)) |
| return false; |
| } |
| } |
| |
| OTC.setAllOperandTypes(); |
| 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; |
| } |
| |
| 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; |
| } |
| |
| 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() |
| : makeAnonPatName(AnonPatNamePrefix, I); |
| |
| 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, 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, Name); |
| } else if (const DagInit *PFP = |
| getDagWithOperatorOfSubClass(Arg, PatFragClassName)) { |
| 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, Name); |
| } else if (const DagInit *BP = |
| getDagWithOperatorOfSubClass(Arg, BuiltinInstClassName)) { |
| Pat = std::make_unique<BuiltinPattern>( |
| *BP->getOperatorAsDef(RuleDef.getLoc()), Name); |
| } else { |
| return nullptr; |
| } |
| |
| for (unsigned K = 0; K < DagPat->getNumArgs(); ++K) { |
| if (!parseInstructionPatternOperand(*Pat, DagPat->getArg(K), |
| 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>(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(), Name, /*Type=*/nullptr); |
| return true; |
| } |
| |
| // typed immediate, e.g. (i32 0) |
| if (const auto *DagOp = dyn_cast<DagInit>(OpInit)) { |
| if (DagOp->getNumArgs() != 1) |
| return ParseErr(); |
| |
| Record *ImmTy = DagOp->getOperatorAsDef(RuleDef.getLoc()); |
| if (!ImmTy->isSubClassOf("ValueType")) { |
| PrintError("cannot parse immediate '" + OpInit->getAsUnquotedString() + |
| "', '" + ImmTy->getName() + "' is not a ValueType!"); |
| 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(), 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(); |
| if (!Def->isSubClassOf("ValueType")) { |
| PrintError("invalid operand type: '" + Def->getName() + |
| "' is not a ValueType"); |
| return false; |
| } |
| IP.addOperand(OpName->getAsUnquotedString(), Def); |
| 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(OpName->getAsUnquotedString(), /*Type=*/nullptr); |
| return true; |
| } |
| |
| return ParseErr(); |
| } |
| |
| std::unique_ptr<PatFrag> |
| CombineRuleBuilder::parsePatFragImpl(const Record *Def) const { |
| auto StackTrace = PrettyStackTraceParse(*Def); |
| if (!Def->isSubClassOf(PatFragClassName)) |
| return nullptr; |
| |
| const DagInit *Ins = Def->getValueAsDag("InOperands"); |
| if (Ins->getOperatorAsDef(Def->getLoc())->getName() != "ins") { |
| ::PrintError(Def, "expected 'ins' operator for " + PatFragClassName + |
| " in operands list"); |
| return nullptr; |
| } |
| |
| const DagInit *Outs = Def->getValueAsDag("OutOperands"); |
| if (Outs->getOperatorAsDef(Def->getLoc())->getName() != "outs") { |
| ::PrintError(Def, "expected 'outs' operator for " + PatFragClassName + |
| " out operands list"); |
| return nullptr; |
| } |
| |
| auto Result = std::make_unique<PatFrag>(*Def); |
| if (!parsePatFragParamList(Def->getLoc(), *Outs, |
| [&](StringRef Name, PatFrag::ParamKind Kind) { |
| Result->addOutParam(Name, Kind); |
| return true; |
| })) |
| return nullptr; |
| |
| if (!parsePatFragParamList(Def->getLoc(), *Ins, |
| [&](StringRef Name, PatFrag::ParamKind Kind) { |
| Result->addInParam(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 " + PatFragClassName + " '" + |
| 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("root"); |
| 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((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()->getName() + "', but '" + PFName + |
| "' constrains it to '" + O.getType()->getName() + "'"); |
| 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 Record *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(), getLLTCodeGenFromRecord(Ty), TempRegID); |
| } |
| |
| DstMI.addRenderer<TempRegRenderer>(TempRegID); |
| } |
| |
| // TODO: works? |
| 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 Record *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; |
| } |
| |
| LLTCodeGen LLT = getLLTCodeGenFromRecord(Ty); |
| if (isGConstant) { |
| DstMI.addRenderer<ImmRenderer>(O.getImmValue(), LLT); |
| return true; |
| } |
| |
| unsigned TempRegID = M.allocateTempRegID(); |
| auto ActIt = M.insertAction<BuildMIAction>( |
| M.actions_begin(), M.allocateOutputInsnID(), &getGConstant()); |
| // Ensure MakeTempReg occurs before the BuildMI of th G_CONSTANT. |
| M.insertAction<MakeTempRegisterAction>(ActIt, LLT, TempRegID); |
| auto &ConstantMI = *static_cast<BuildMIAction *>(ActIt->get()); |
| ConstantMI.addRenderer<TempRegRenderer>(TempRegID); |
| ConstantMI.addRenderer<ImmRenderer>(O.getImmValue(), LLT); |
| 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()); |
| |
| 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 Record *Ty = RemappedO.getType()) |
| OM.addPredicate<LLTOperandMatcher>(getLLTCodeGenFromRecord(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; |
| |
| 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" |
| << " NewMIVector OutMIs;\n" |
| << " State.MIs.clear();\n" |
| << " State.MIs.push_back(&I);\n" |
| << " " << MatchDataInfo::StructName << " = " |
| << MatchDataInfo::StructTypeName << "();\n\n" |
| << " if (executeMatchTable(*this, OutMIs, State, ExecInfo" |
| << ", getMatchTable(), *ST.getInstrInfo(), MRI, " |
| "*MRI.getTargetRegisterInfo(), *ST.getRegBankInfo(), AvailableFeatures" |
| << ", /*CoverageInfo*/ nullptr, &Observer)) {\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")) { |
| 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; |
| } |
| } else |
| gatherRules(ActiveRules, Rec->getValueAsListOfDefs("Rules")); |
| } |
| } |
| |
| 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"); |