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