//===- 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 using namespace llvm; using namespace llvm::gi; #define DEBUG_TYPE "gicombiner-emitter" namespace { cl::OptionCategory GICombinerEmitterCat("Options for -gen-global-isel-combiner"); cl::opt StopAfterParse( "gicombiner-stop-after-parse", cl::desc("Stop processing after parsing rules and dump state"), cl::cat(GICombinerEmitterCat)); cl::list SelectedCombiners("combiners", cl::desc("Emit the specified combiners"), cl::cat(GICombinerEmitterCat), cl::CommaSeparated); cl::opt 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 auto keys(Container &&C) { return map_range(C, [](auto &Entry) -> auto & { return Entry.first; }); } template auto values(Container &&C) { return map_range(C, [](auto &Entry) -> auto & { return Entry.second; }); } 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() ? "" : VarName) << ")"; } /// Pool of type -> variables used to emit MatchData variables declarations. /// /// e.g. if the map contains "int64_t" -> ["MD0", "MD1"], then two variable /// declarations must be emitted: `int64_t MD0` and `int64_t MD1`. /// /// This has a static lifetime and will outlive all the `MatchDataInfo` objects /// by design. It needs to persist after all `CombineRuleBuilder` objects died /// so we can emit the variable declarations. StringMap> AllMatchDataVars; // Assign variable names to all MatchDatas used by a pattern. This must be // called after all MatchData decls have been parsed inside a rule. // // Requires an array of MatchDataInfo so we can handle cases where a pattern // uses multiple instances of the same MatchData type. void AssignMatchDataVariables(MutableArrayRef Infos) { static unsigned NextVarID = 0; StringMap SeenTypes; for (auto &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>; static CXXPredicateCodePool AllCXXMatchCode; static CXXPredicateCodePool AllCXXApplyCode; /// Sorts a `CXXPredicateCodePool` by their IDs and returns it. static std::vector getSorted(const CXXPredicateCodePool &Pool) { std::vector Out; std::transform(Pool.begin(), Pool.end(), std::back_inserter(Out), [&](auto &Elt) { return Elt.second.get(); }); sort(Out, [](const auto *A, const auto *B) { return A->ID < B->ID; }); return Out; } /// 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( 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 getAllMatchCode() { return getSorted(AllCXXMatchCode); } static std::vector 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 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 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 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 Locs, function_ref 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 Locs, function_ref 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 << ""; 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 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 void addOperand(Ty &&...Init) { Operands.emplace_back(std::forward(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 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 Locs) const; virtual bool checkSemantics(ArrayRef Loc); void print(raw_ostream &OS, bool PrintName = true) const override; protected: InstructionPattern(unsigned K, StringRef Name) : Pattern(K, Name) {} SmallVector Operands; }; void InstructionPattern::reportUnreachable(ArrayRef Locs) const { PrintError(Locs, "pattern '" + getName() + "' ('" + getInstName() + "') is unreachable from the pattern root!"); } bool InstructionPattern::checkSemantics(ArrayRef 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 class OperandTable { public: static_assert(std::is_base_of_v, "DefTy should be a derived class from InstructionPattern"); bool addPattern(DefTy *P, function_ref 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 << ")\n"; return; } SmallVector Keys(Table.keys()); sort(Keys); OS << "\n"; for (const auto &Key : Keys) { const auto *Def = Table.at(Key); OS << Indent << " " << Key << " -> " << (Def ? Def->getName() : "") << "\n"; } OS << Indent << ")\n"; } auto begin() const { return Table.begin(); } auto end() const { return Table.end(); } void dump() const { print(dbgs()); } private: StringMap 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(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(I.Operands.NumDefs, Operands.size() - NumOuts); } unsigned CodeGenInstructionPattern::getNumInstOperands() const { unsigned NumCGIOps = I.Operands.size(); return isVariadic() ? std::max(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 DiagLoc) : DiagLoc(DiagLoc) {} bool check(InstructionPattern *P); void setAllOperandTypes(); private: struct OpTypeInfo { const Record *Type = nullptr; InstructionPattern *TypeSrc = nullptr; }; ArrayRef DiagLoc; StringMap Types; SmallVector 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; 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, 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 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 in_params() const; unsigned num_in_params() const { return Params.size() - NumOutParams; } void addOutParam(StringRef Name, ParamKind Kind); iterator_range 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 DiagLoc) const; bool checkSemantics(); bool buildOperandsTables(); private: static void printParamsList(raw_ostream &OS, iterator_range Params); void PrintError(Twine Msg) const { ::PrintError(&Def, Msg); } const Record &Def; unsigned NumOutParams = 0; ParamVec Params; SmallVector 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::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::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(Pat.get())) { if (!OTC.check(IP)) return false; } } OTC.setAllOperandTypes(); } return true; } bool PatFrag::handleUnboundInParam(StringRef ParamName, StringRef ArgName, ArrayRef 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(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 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 getApplyDefsNeeded() const override; bool checkSemantics(ArrayRef 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 DiagLoc) const; private: const PatFrag &PF; }; ArrayRef 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 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 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 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 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 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>; using PatternAlternatives = DenseMap; CombineRuleBuilder(const CodeGenTarget &CGT, SubtargetFeatureInfoMap &SubtargetFeatures, Record &RuleDef, unsigned ID, std::vector &OutRMs) : CGT(CGT), SubtargetFeatures(SubtargetFeatures), RuleDef(RuleDef), RuleID(ID), OutRMs(OutRMs) {} /// Parses all fields in the RuleDef record. bool parseAll(); /// Emits all RuleMatchers into the vector of RuleMatchers passed in the /// constructor. bool emitRuleMatchers(); void print(raw_ostream &OS) const; void dump() const { print(dbgs()); } /// Debug-only verification of invariants. #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 Pat); bool addMatchPattern(std::unique_ptr 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)> ParseAction, StringRef Operator, ArrayRef DiagLoc, StringRef AnonPatNamePrefix) const; std::unique_ptr parseInstructionPattern(const Init &Arg, StringRef PatName) const; std::unique_ptr parseWipMatchOpcodeMatcher(const Init &Arg, StringRef PatName) const; bool parseInstructionPatternOperand(InstructionPattern &IP, const Init *OpInit, const StringInit *OpName) const; std::unique_ptr parsePatFragImpl(const Record *Def) const; bool parsePatFragParamList( ArrayRef DiagLoc, const DagInit &OpsList, function_ref 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 &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 &SeenPats, StringMap &OperandToTempRegID); bool emitCodeGenInstructionApplyImmOperand(RuleMatcher &M, BuildMIAction &DstMI, const CodeGenInstructionPattern &P, const InstructionOperand &O); bool emitBuiltinApplyPattern(CodeExpansions &CE, RuleMatcher &M, const BuiltinPattern &P, StringMap &OperandToTempRegID); // Recursively visits CodeGenInstructionPattern from P to build up the // RuleMatcher/InstructionMatcher. May create new InstructionMatchers as // needed. using OperandMapperFnRef = function_ref; using OperandDefLookupFn = function_ref; bool emitCodeGenInstructionMatchPattern( CodeExpansions &CE, const PatternAlternatives &Alts, RuleMatcher &M, InstructionMatcher &IM, const CodeGenInstructionPattern &P, DenseSet &SeenPats, OperandDefLookupFn LookupOperandDef, OperandMapperFnRef OperandMapper = [](const auto &O) { return O; }); const CodeGenTarget &CGT; SubtargetFeatureInfoMap &SubtargetFeatures; Record &RuleDef; const unsigned RuleID; std::vector &OutRMs; // For InstructionMatcher::addOperand unsigned AllocatedTemporariesBaseID = 0; /// The root of the pattern. StringRef RootName; /// These maps have ownership of the actual Pattern objects. /// They both map a Pattern's name to the Pattern instance. PatternMap MatchPats; PatternMap ApplyPats; /// Operand tables to tie match/apply patterns together. OperandTable<> MatchOpTable; OperandTable<> ApplyOpTable; /// Set by findRoots. Pattern *MatchRoot = nullptr; SmallDenseSet ApplyRoots; SmallVector MatchDatas; SmallVector PermutationsToEmit; // print()/debug-only members. mutable SmallPtrSet 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(MatchRoot))) return false; break; } case Pattern::K_PatFrag: case Pattern::K_Builtin: case Pattern::K_CodeGenInstruction: if (!emitMatchPattern(CE, Alts, *cast(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 << ")\n"; return; } OS << "\n"; for (const auto &[Name, Pat] : Pats) { OS << " "; if (Pat.get() == MatchRoot) OS << ""; if (isa(Pat.get()) && ApplyRoots.contains(cast(Pat.get()))) OS << ""; 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(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 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 Pat) { StringRef Name = Pat->getName(); if (ApplyPats.contains(Name)) { PrintError("'" + Name + "' apply pattern defined more than once!"); return false; } if (isa(Pat.get())) { PrintError("'" + Name + "': wip_match_opcode is not supported in apply patterns"); return false; } if (isa(Pat.get())) { PrintError("'" + Name + "': using " + PatFragClassName + " is not supported in apply patterns"); return false; } if (auto *CXXPat = dyn_cast(Pat.get())) CXXPat->setIsApply(); ApplyPats[Name] = std::move(Pat); return true; } bool CombineRuleBuilder::addMatchPattern(std::unique_ptr 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(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( ExpandedCode.getEnumNameWithPrefix(CXXPredPrefix)); } void CombineRuleBuilder::addCXXAction(RuleMatcher &M, const CodeExpansions &CE, const CXXPattern &P) { const auto &ExpandedCode = P.expandCode(CE, RuleDef.getLoc()); M.addAction( ExpandedCode.getEnumNameWithPrefix(CXXApplyPrefix)); } bool CombineRuleBuilder::hasOnlyCXXApplyPatterns() const { return all_of(ApplyPats, [&](auto &Entry) { return isa(Entry.second.get()); }); } bool CombineRuleBuilder::hasEraseRoot() const { return any_of(ApplyPats, [&](auto &Entry) { if (const auto *BP = dyn_cast(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(Pat.get())) { if (!OTC.check(IP)) return false; } } for (auto &Pat : values(ApplyPats)) { if (auto *IP = dyn_cast(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(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(Pat)) { if (!CXXPat->getRawCode().contains("return ")) PrintWarning("'match' C++ code does not seem to return!"); continue; } const auto *AOP = dyn_cast(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(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(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(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(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(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(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(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(Pat.get()); if (IP && !MatchOpTable.addPattern(IP, DiagnoseRedefMatch)) return false; } for (auto &Pat : values(ApplyPats)) { auto *IP = dyn_cast(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 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)> ParseAction, StringRef Operator, ArrayRef 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(Arg)) { auto CXXPat = std::make_unique(*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 CombineRuleBuilder::parseInstructionPattern(const Init &Arg, StringRef Name) const { const DagInit *DagPat = dyn_cast(&Arg); if (!DagPat) return nullptr; std::unique_ptr Pat; if (const DagInit *IP = getDagWithOperatorOfSubClass(Arg, "Instruction")) { auto &Instr = CGT.getInstruction(IP->getOperatorAsDef(RuleDef.getLoc())); Pat = std::make_unique(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(*PF, Name); } else if (const DagInit *BP = getDagWithOperatorOfSubClass(Arg, BuiltinInstClassName)) { Pat = std::make_unique( *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 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(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(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(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(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(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(OpInit)) { assert(OpName && "Unset w/ no OpName?"); IP.addOperand(OpName->getAsUnquotedString(), /*Type=*/nullptr); return true; } return ParseErr(); } std::unique_ptr 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(*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(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 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 DiagLoc, const DagInit &OpsList, function_ref 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(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> ParsedPatFrags; auto It = ParsedPatFrags.find(Def); if (It != ParsedPatFrags.end()) { SeenPatFrags.insert(It->second.get()); return It->second.get(); } std::unique_ptr 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 SeenPats; const auto FindOperandDef = [&](StringRef Op) -> InstructionPattern * { return MatchOpTable.getDef(Op); }; if (const auto *CGP = dyn_cast(&IP)) { if (!emitCodeGenInstructionMatchPattern(CE, Alts, M, IM, *CGP, SeenPats, FindOperandDef)) return false; } else if (const auto *PFP = dyn_cast(&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(&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(Pat.get()), SeenPats)) return false; continue; } case Pattern::K_Builtin: PrintError("No known match builtins"); return false; case Pattern::K_CodeGenInstruction: cast(Pat.get())->reportUnreachable(RuleDef.getLoc()); return false; case Pattern::K_CXX: { addCXXPredicate(M, CE, *cast(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(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 SeenPats; if (!emitPatFragMatchPattern(CE, Alts, M, /*IM*/ nullptr, *cast(Pat.get()), SeenPats)) return false; continue; } case Pattern::K_Builtin: PrintError("No known match builtins"); return false; case Pattern::K_CodeGenInstruction: cast(Pat.get())->reportUnreachable( RuleDef.getLoc()); return false; case Pattern::K_CXX: { addCXXPredicate(M, CE, *cast(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 &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, 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 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(Def) && "Nested PatFrags not supported yet"); if (!emitCodeGenInstructionMatchPattern( PatFragCEs, Alts, RM, *IM, *cast(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(Pat.get())) { addCXXPredicate(RM, PatFragCEs, *CXXPat, Alts); continue; } if (const auto *IP = dyn_cast(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(Pat.get())); return true; } DenseSet SeenPats; StringMap OperandToTempRegID; for (auto *ApplyRoot : ApplyRoots) { assert(isa(ApplyRoot) && "Root can only be a InstructionPattern!"); if (!emitInstructionApplyPattern(CE, M, cast(*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(*Pat), SeenPats, OperandToTempRegID)) return false; break; case Pattern::K_CodeGenInstruction: cast(*Pat).reportUnreachable(RuleDef.getLoc()); return false; case Pattern::K_CXX: { addCXXAction(M, CE, *cast(Pat.get())); continue; } default: llvm_unreachable("unknown pattern kind!"); } } return true; } bool CombineRuleBuilder::emitInstructionApplyPattern( CodeExpansions &CE, RuleMatcher &M, const InstructionPattern &P, DenseSet &SeenPats, StringMap &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(&P)) return emitBuiltinApplyPattern(CE, M, *BP, OperandToTempRegID); if (isa(&P)) llvm_unreachable("PatFragPatterns is not supported in 'apply'!"); auto &CGIP = cast(P); // Now render this inst. auto &DstMI = M.addAction(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(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(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(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( M.actions_begin(), getLLTCodeGenFromRecord(Ty), TempRegID); } DstMI.addRenderer(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(O.getImmValue()); return true; } LLTCodeGen LLT = getLLTCodeGenFromRecord(Ty); if (isGConstant) { DstMI.addRenderer(O.getImmValue(), LLT); return true; } unsigned TempRegID = M.allocateTempRegID(); auto ActIt = M.insertAction( M.actions_begin(), M.allocateOutputInsnID(), &getGConstant()); // Ensure MakeTempReg occurs before the BuildMI of th G_CONSTANT. M.insertAction(ActIt, LLT, TempRegID); auto &ConstantMI = *static_cast(ActIt->get()); ConstantMI.addRenderer(TempRegID); ConstantMI.addRenderer(O.getImmValue(), LLT); DstMI.addRenderer(TempRegID); return true; } bool CombineRuleBuilder::emitBuiltinApplyPattern( CodeExpansions &CE, RuleMatcher &M, const BuiltinPattern &P, StringMap &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(/*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(OldOM.getInsnVarID(), OldOM.getOpIdx(), It->second); } else { // Replace with matched reg. auto &NewOM = M.getOperandMatcher(New); M.addAction(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(/*InsnID*/ 0); return true; } } llvm_unreachable("Unknown BuiltinKind!"); } bool isLiteralImm(const InstructionPattern &P, unsigned OpIdx) { if (const auto *CGP = dyn_cast(&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 &SeenPats, OperandDefLookupFn LookupOperandDef, OperandMapperFnRef OperandMapper) { auto StackTrace = PrettyStackTraceEmit(RuleDef, &P); if (SeenPats.contains(&P)) return true; SeenPats.insert(&P); IM.addPredicate(&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(RemappedO.getImmValue()); else OM.addPredicate(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()) { if (const Record *Ty = RemappedO.getType()) OM.addPredicate(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(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(DefPat)) { if (!emitCodeGenInstructionMatchPattern(CE, Alts, M, IM, *CGIDef, SeenPats, LookupOperandDef, OperandMapper)) return false; continue; } if (const auto *PFPDef = dyn_cast(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> AllCombineRules; MatchTable buildMatchTable(MutableArrayRef Rules); void emitRuleConfigImpl(raw_ostream &OS); void emitAdditionalImpl(raw_ostream &OS) override; void emitMIPredicateFns(raw_ostream &OS) override; void emitI64ImmPredicateFns(raw_ostream &OS) override; void emitAPFloatImmPredicateFns(raw_ostream &OS) override; void emitAPIntImmPredicateFns(raw_ostream &OS) override; void emitTestSimplePredicate(raw_ostream &OS) override; void emitRunCustomAction(raw_ostream &OS) override; void emitAdditionalTemporariesDecl(raw_ostream &OS, StringRef Indent) override; const CodeGenTarget &getTarget() const override { return Target; } StringRef getClassName() const override { return Combiner->getValueAsString("Classname"); } StringRef getCombineAllMethodName() const { return Combiner->getValueAsString("CombineAllMethodName"); } std::string getRuleConfigClassName() const { return getClassName().str() + "RuleConfig"; } void gatherRules(std::vector &Rules, const std::vector &&RulesAndGroups); public: explicit GICombinerEmitter(RecordKeeper &RK, const CodeGenTarget &Target, StringRef Name, Record *Combiner); ~GICombinerEmitter() {} void run(raw_ostream &OS); }; void GICombinerEmitter::emitRuleConfigImpl(raw_ostream &OS) { OS << "struct " << getRuleConfigClassName() << " {\n" << " SparseBitVector<> DisabledRules;\n\n" << " bool isRuleEnabled(unsigned RuleID) const;\n" << " bool parseCommandLineOption();\n" << " bool setRuleEnabled(StringRef RuleIdentifier);\n" << " bool setRuleDisabled(StringRef RuleIdentifier);\n" << "};\n\n"; std::vector> Cases; Cases.reserve(AllCombineRules.size()); for (const auto &[ID, Name] : AllCombineRules) Cases.emplace_back(Name, "return " + to_string(ID) + ";\n"); OS << "static std::optional getRuleIdxForIdentifier(StringRef " "RuleIdentifier) {\n" << " uint64_t I;\n" << " // getAtInteger(...) returns false on success\n" << " bool Parsed = !RuleIdentifier.getAsInteger(0, I);\n" << " if (Parsed)\n" << " return I;\n\n" << "#ifndef NDEBUG\n"; StringMatcher Matcher("RuleIdentifier", Cases, OS); Matcher.Emit(); OS << "#endif // ifndef NDEBUG\n\n" << " return std::nullopt;\n" << "}\n"; OS << "static std::optional> " "getRuleRangeForIdentifier(StringRef RuleIdentifier) {\n" << " std::pair RangePair = " "RuleIdentifier.split('-');\n" << " if (!RangePair.second.empty()) {\n" << " const auto First = " "getRuleIdxForIdentifier(RangePair.first);\n" << " const auto Last = " "getRuleIdxForIdentifier(RangePair.second);\n" << " if (!First || !Last)\n" << " return std::nullopt;\n" << " if (First >= Last)\n" << " report_fatal_error(\"Beginning of range should be before " "end of range\");\n" << " return {{*First, *Last + 1}};\n" << " }\n" << " if (RangePair.first == \"*\") {\n" << " return {{0, " << AllCombineRules.size() << "}};\n" << " }\n" << " const auto I = getRuleIdxForIdentifier(RangePair.first);\n" << " if (!I)\n" << " return std::nullopt;\n" << " return {{*I, *I + 1}};\n" << "}\n\n"; for (bool Enabled : {true, false}) { OS << "bool " << getRuleConfigClassName() << "::setRule" << (Enabled ? "Enabled" : "Disabled") << "(StringRef RuleIdentifier) {\n" << " auto MaybeRange = getRuleRangeForIdentifier(RuleIdentifier);\n" << " if (!MaybeRange)\n" << " return false;\n" << " for (auto I = MaybeRange->first; I < MaybeRange->second; ++I)\n" << " DisabledRules." << (Enabled ? "reset" : "set") << "(I);\n" << " return true;\n" << "}\n\n"; } OS << "static std::vector " << Name << "Option;\n" << "static cl::list " << Name << "DisableOption(\n" << " \"" << Name.lower() << "-disable-rule\",\n" << " cl::desc(\"Disable one or more combiner rules temporarily in " << "the " << Name << " pass\"),\n" << " cl::CommaSeparated,\n" << " cl::Hidden,\n" << " cl::cat(GICombinerOptionCategory),\n" << " cl::callback([](const std::string &Str) {\n" << " " << Name << "Option.push_back(Str);\n" << " }));\n" << "static cl::list " << Name << "OnlyEnableOption(\n" << " \"" << Name.lower() << "-only-enable-rule\",\n" << " cl::desc(\"Disable all rules in the " << Name << " pass then re-enable the specified ones\"),\n" << " cl::Hidden,\n" << " cl::cat(GICombinerOptionCategory),\n" << " cl::callback([](const std::string &CommaSeparatedArg) {\n" << " StringRef Str = CommaSeparatedArg;\n" << " " << Name << "Option.push_back(\"*\");\n" << " do {\n" << " auto X = Str.split(\",\");\n" << " " << Name << "Option.push_back((\"!\" + X.first).str());\n" << " Str = X.second;\n" << " } while (!Str.empty());\n" << " }));\n" << "\n\n" << "bool " << getRuleConfigClassName() << "::isRuleEnabled(unsigned RuleID) const {\n" << " return !DisabledRules.test(RuleID);\n" << "}\n" << "bool " << getRuleConfigClassName() << "::parseCommandLineOption() {\n" << " for (StringRef Identifier : " << Name << "Option) {\n" << " bool Enabled = Identifier.consume_front(\"!\");\n" << " if (Enabled && !setRuleEnabled(Identifier))\n" << " return false;\n" << " if (!Enabled && !setRuleDisabled(Identifier))\n" << " return false;\n" << " }\n" << " return true;\n" << "}\n\n"; } void GICombinerEmitter::emitAdditionalImpl(raw_ostream &OS) { OS << "bool " << getClassName() << "::" << 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( OS, "", ArrayRef(MatchCode), [](const CXXPredicateCode *C) -> StringRef { return C->BaseEnumName; }, [](const CXXPredicateCode *C) -> StringRef { return C->Code; }); } void GICombinerEmitter::emitI64ImmPredicateFns(raw_ostream &OS) { // Unused, but still needs to be called. emitImmPredicateFnsImpl( OS, "I64", "int64_t", {}, [](unsigned) { return ""; }, [](unsigned) { return ""; }); } void GICombinerEmitter::emitAPFloatImmPredicateFns(raw_ostream &OS) { // Unused, but still needs to be called. emitImmPredicateFnsImpl( OS, "APFloat", "const APFloat &", {}, [](unsigned) { return ""; }, [](unsigned) { return ""; }); } void GICombinerEmitter::emitAPIntImmPredicateFns(raw_ostream &OS) { // Unused, but still needs to be called. emitImmPredicateFnsImpl( OS, "APInt", "const APInt &", {}, [](unsigned) { return ""; }, [](unsigned) { return ""; }); } void GICombinerEmitter::emitTestSimplePredicate(raw_ostream &OS) { if (!AllCombineRules.empty()) { OS << "enum {\n"; std::string EnumeratorSeparator = " = GICXXPred_Invalid + 1,\n"; // To avoid emitting a switch, we expect that all those rules are in order. // That way we can just get the RuleID from the enum by subtracting // (GICXXPred_Invalid + 1). unsigned ExpectedID = 0; (void)ExpectedID; for (const auto &ID : keys(AllCombineRules)) { assert(ExpectedID++ == ID && "combine rules are not ordered!"); OS << " " << getIsEnabledPredicateEnumName(ID) << EnumeratorSeparator; EnumeratorSeparator = ",\n"; } OS << "};\n\n"; } OS << "bool " << getClassName() << "::testSimplePredicate(unsigned Predicate) const {\n" << " return RuleConfig.isRuleEnabled(Predicate - " "GICXXPred_Invalid - " "1);\n" << "}\n"; } void GICombinerEmitter::emitRunCustomAction(raw_ostream &OS) { const auto ApplyCode = 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 Rules) { std::vector InputRules; for (Matcher &Rule : Rules) InputRules.push_back(&Rule); unsigned CurrentOrdering = 0; StringMap OpcodeOrder; for (RuleMatcher &Rule : Rules) { const StringRef Opcode = Rule.getOpcode(); assert(!Opcode.empty() && "Didn't expect an undefined opcode"); if (OpcodeOrder.count(Opcode) == 0) OpcodeOrder[Opcode] = CurrentOrdering++; } llvm::stable_sort(InputRules, [&OpcodeOrder](const Matcher *A, const Matcher *B) { auto *L = static_cast(A); auto *R = static_cast(B); return std::make_tuple(OpcodeOrder[L->getOpcode()], L->getNumOperands()) < std::make_tuple(OpcodeOrder[R->getOpcode()], R->getNumOperands()); }); for (Matcher *Rule : InputRules) Rule->optimize(); std::vector> MatcherStorage; std::vector OptRules = optimizeRules(InputRules, MatcherStorage); for (Matcher *Rule : OptRules) Rule->optimize(); OptRules = optimizeRules(OptRules, MatcherStorage); return MatchTable::buildTable(OptRules, /*WithCoverage*/ false, /*IsCombiner*/ true); } /// Recurse into GICombineGroup's and flatten the ruleset into a simple list. void GICombinerEmitter::gatherRules( std::vector &ActiveRules, const std::vector &&RulesAndGroups) { for (Record *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) { Records.startTimer("Gather rules"); std::vector 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()); const MatchTable Table = buildMatchTable(Rules); Records.startTimer("Emit combiner"); emitSourceFileHeader(getClassName().str() + " Combiner Match Table", OS); // Unused std::vector CustomRendererFns; // Unused std::vector ComplexPredicates; SmallVector 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");