//===- GlobalISelMatchTable.h ---------------------------------------------===// // // 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 /// This file contains the code related to the GlobalISel Match Table emitted by /// GlobalISelEmitter.cpp. The generated match table is interpreted at runtime /// by `GIMatchTableExecutorImpl.h` to match & apply ISel patterns. /// //===----------------------------------------------------------------------===// #ifndef LLVM_UTILS_TABLEGEN_GLOBALISELMATCHTABLE_H #define LLVM_UTILS_TABLEGEN_GLOBALISELMATCHTABLE_H #include "Common/CodeGenDAGPatterns.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/CodeGenTypes/LowLevelType.h" #include "llvm/Support/Error.h" #include "llvm/Support/SaveAndRestore.h" #include #include #include #include #include #include #include #include namespace llvm { class raw_ostream; class Record; class SMLoc; class CodeGenRegisterClass; // Use a namespace to avoid conflicts because there's some fairly generic names // in there (e.g. Matcher). namespace gi { class MatchTable; class Matcher; class OperandMatcher; class MatchAction; class PredicateMatcher; class InstructionMatcher; enum { GISF_IgnoreCopies = 0x1, }; using GISelFlags = std::uint16_t; //===- Helper functions ---------------------------------------------------===// void emitEncodingMacrosDef(raw_ostream &OS); void emitEncodingMacrosUndef(raw_ostream &OS); std::string getNameForFeatureBitset(const std::vector &FeatureBitset, int HwModeIdx); /// Takes a sequence of \p Rules and group them based on the predicates /// they share. \p MatcherStorage is used as a memory container /// for the group that are created as part of this process. /// /// What this optimization does looks like if GroupT = GroupMatcher: /// Output without optimization: /// \verbatim /// # R1 /// # predicate A /// # predicate B /// ... /// # R2 /// # predicate A // <-- effectively this is going to be checked twice. /// // Once in R1 and once in R2. /// # predicate C /// \endverbatim /// Output with optimization: /// \verbatim /// # Group1_2 /// # predicate A // <-- Check is now shared. /// # R1 /// # predicate B /// # R2 /// # predicate C /// \endverbatim template std::vector optimizeRules(ArrayRef Rules, std::vector> &MatcherStorage); /// A record to be stored in a MatchTable. /// /// This class represents any and all output that may be required to emit the /// MatchTable. Instances are most often configured to represent an opcode or /// value that will be emitted to the table with some formatting but it can also /// represent commas, comments, and other formatting instructions. struct MatchTableRecord { enum RecordFlagsBits { MTRF_None = 0x0, /// Causes EmitStr to be formatted as comment when emitted. MTRF_Comment = 0x1, /// Causes the record value to be followed by a comma when emitted. MTRF_CommaFollows = 0x2, /// Causes the record value to be followed by a line break when emitted. MTRF_LineBreakFollows = 0x4, /// Indicates that the record defines a label and causes an additional /// comment to be emitted containing the index of the label. MTRF_Label = 0x8, /// Causes the record to be emitted as the index of the label specified by /// LabelID along with a comment indicating where that label is. MTRF_JumpTarget = 0x10, /// Causes the formatter to add a level of indentation before emitting the /// record. MTRF_Indent = 0x20, /// Causes the formatter to remove a level of indentation after emitting the /// record. MTRF_Outdent = 0x40, /// Causes the formatter to not use encoding macros to emit this multi-byte /// value. MTRF_PreEncoded = 0x80, }; /// When MTRF_Label or MTRF_JumpTarget is used, indicates a label id to /// reference or define. unsigned LabelID; /// The string to emit. Depending on the MTRF_* flags it may be a comment, a /// value, a label name. std::string EmitStr; private: /// The number of MatchTable elements described by this record. Comments are 0 /// while values are typically 1. Values >1 may occur when we need to emit /// values that exceed the size of a MatchTable element. unsigned NumElements; public: /// A bitfield of RecordFlagsBits flags. unsigned Flags; /// The actual run-time value, if known int64_t RawValue; MatchTableRecord(std::optional LabelID_, StringRef EmitStr, unsigned NumElements, unsigned Flags, int64_t RawValue = std::numeric_limits::min()) : LabelID(LabelID_.value_or(~0u)), EmitStr(EmitStr), NumElements(NumElements), Flags(Flags), RawValue(RawValue) { assert((!LabelID_ || LabelID != ~0u) && "This value is reserved for non-labels"); } MatchTableRecord(const MatchTableRecord &Other) = default; MatchTableRecord(MatchTableRecord &&Other) = default; /// Useful if a Match Table Record gets optimized out void turnIntoComment() { Flags |= MTRF_Comment; Flags &= ~MTRF_CommaFollows; NumElements = 0; } /// For Jump Table generation purposes bool operator<(const MatchTableRecord &Other) const { return RawValue < Other.RawValue; } int64_t getRawValue() const { return RawValue; } void emit(raw_ostream &OS, bool LineBreakNextAfterThis, const MatchTable &Table) const; unsigned size() const { return NumElements; } }; /// Holds the contents of a generated MatchTable to enable formatting and the /// necessary index tracking needed to support GIM_Try. class MatchTable { /// An unique identifier for the table. The generated table will be named /// MatchTable${ID}. unsigned ID; /// The records that make up the table. Also includes comments describing the /// values being emitted and line breaks to format it. std::vector Contents; /// The currently defined labels. DenseMap LabelMap; /// Tracks the sum of MatchTableRecord::NumElements as the table is built. unsigned CurrentSize = 0; /// A unique identifier for a MatchTable label. unsigned CurrentLabelID = 0; /// Determines if the table should be instrumented for rule coverage tracking. bool IsWithCoverage; /// Whether this table is for the GISel combiner. bool IsCombinerTable; public: static MatchTableRecord LineBreak; static MatchTableRecord Comment(StringRef Comment); static MatchTableRecord Opcode(StringRef Opcode, int IndentAdjust = 0); static MatchTableRecord NamedValue(unsigned NumBytes, StringRef NamedValue); static MatchTableRecord NamedValue(unsigned NumBytes, StringRef NamedValue, int64_t RawValue); static MatchTableRecord NamedValue(unsigned NumBytes, StringRef Namespace, StringRef NamedValue); static MatchTableRecord NamedValue(unsigned NumBytes, StringRef Namespace, StringRef NamedValue, int64_t RawValue); static MatchTableRecord IntValue(unsigned NumBytes, int64_t IntValue); static MatchTableRecord ULEB128Value(uint64_t IntValue); static MatchTableRecord Label(unsigned LabelID); static MatchTableRecord JumpTarget(unsigned LabelID); static MatchTable buildTable(ArrayRef Rules, bool WithCoverage, bool IsCombiner = false); MatchTable(bool WithCoverage, bool IsCombinerTable, unsigned ID = 0) : ID(ID), IsWithCoverage(WithCoverage), IsCombinerTable(IsCombinerTable) { } bool isWithCoverage() const { return IsWithCoverage; } bool isCombiner() const { return IsCombinerTable; } void push_back(const MatchTableRecord &Value) { if (Value.Flags & MatchTableRecord::MTRF_Label) defineLabel(Value.LabelID); Contents.push_back(Value); CurrentSize += Value.size(); } unsigned allocateLabelID() { return CurrentLabelID++; } void defineLabel(unsigned LabelID) { LabelMap.insert(std::pair(LabelID, CurrentSize)); } unsigned getLabelIndex(unsigned LabelID) const { const auto I = LabelMap.find(LabelID); assert(I != LabelMap.end() && "Use of undeclared label"); return I->second; } void emitUse(raw_ostream &OS) const; void emitDeclaration(raw_ostream &OS) const; }; inline MatchTable &operator<<(MatchTable &Table, const MatchTableRecord &Value) { Table.push_back(Value); return Table; } /// This class stands in for LLT wherever we want to tablegen-erate an /// equivalent at compiler run-time. class LLTCodeGen { private: LLT Ty; public: LLTCodeGen() = default; LLTCodeGen(const LLT &Ty) : Ty(Ty) {} std::string getCxxEnumValue() const; void emitCxxEnumValue(raw_ostream &OS) const; void emitCxxConstructorCall(raw_ostream &OS) const; const LLT &get() const { return Ty; } /// This ordering is used for std::unique() and llvm::sort(). There's no /// particular logic behind the order but either A < B or B < A must be /// true if A != B. bool operator<(const LLTCodeGen &Other) const; bool operator==(const LLTCodeGen &B) const { return Ty == B.Ty; } }; // Track all types that are used so we can emit the corresponding enum. extern std::set KnownTypes; /// Convert an MVT to an equivalent LLT if possible, or the invalid LLT() for /// MVTs that don't map cleanly to an LLT (e.g., iPTR, *any, ...). std::optional MVTToLLT(MVT::SimpleValueType SVT); using TempTypeIdx = int64_t; class LLTCodeGenOrTempType { public: LLTCodeGenOrTempType(const LLTCodeGen &LLT) : Data(LLT) {} LLTCodeGenOrTempType(TempTypeIdx TempTy) : Data(TempTy) {} bool isLLTCodeGen() const { return std::holds_alternative(Data); } bool isTempTypeIdx() const { return std::holds_alternative(Data); } const LLTCodeGen &getLLTCodeGen() const { assert(isLLTCodeGen()); return std::get(Data); } TempTypeIdx getTempTypeIdx() const { assert(isTempTypeIdx()); return std::get(Data); } private: std::variant Data; }; inline MatchTable &operator<<(MatchTable &Table, const LLTCodeGenOrTempType &Ty) { if (Ty.isLLTCodeGen()) Table << MatchTable::NamedValue(1, Ty.getLLTCodeGen().getCxxEnumValue()); else Table << MatchTable::IntValue(1, Ty.getTempTypeIdx()); return Table; } //===- Matchers -----------------------------------------------------------===// class Matcher { public: virtual ~Matcher(); virtual void optimize(); virtual void emit(MatchTable &Table) = 0; virtual bool hasFirstCondition() const = 0; virtual const PredicateMatcher &getFirstCondition() const = 0; virtual std::unique_ptr popFirstCondition() = 0; }; class GroupMatcher final : public Matcher { /// Conditions that form a common prefix of all the matchers contained. SmallVector, 1> Conditions; /// All the nested matchers, sharing a common prefix. std::vector Matchers; /// An owning collection for any auxiliary matchers created while optimizing /// nested matchers contained. std::vector> MatcherStorage; public: /// Add a matcher to the collection of nested matchers if it meets the /// requirements, and return true. If it doesn't, do nothing and return false. /// /// Expected to preserve its argument, so it could be moved out later on. bool addMatcher(Matcher &Candidate); /// Mark the matcher as fully-built and ensure any invariants expected by both /// optimize() and emit(...) methods. Generally, both sequences of calls /// are expected to lead to a sensible result: /// /// addMatcher(...)*; finalize(); optimize(); emit(...); and /// addMatcher(...)*; finalize(); emit(...); /// /// or generally /// /// addMatcher(...)*; finalize(); { optimize()*; emit(...); }* /// /// Multiple calls to optimize() are expected to be handled gracefully, though /// optimize() is not expected to be idempotent. Multiple calls to finalize() /// aren't generally supported. emit(...) is expected to be non-mutating and /// producing the exact same results upon repeated calls. /// /// addMatcher() calls after the finalize() call are not supported. /// /// finalize() and optimize() are both allowed to mutate the contained /// matchers, so moving them out after finalize() is not supported. void finalize(); void optimize() override; void emit(MatchTable &Table) override; /// Could be used to move out the matchers added previously, unless finalize() /// has been already called. If any of the matchers are moved out, the group /// becomes safe to destroy, but not safe to re-use for anything else. iterator_range::iterator> matchers() { return make_range(Matchers.begin(), Matchers.end()); } size_t size() const { return Matchers.size(); } bool empty() const { return Matchers.empty(); } std::unique_ptr popFirstCondition() override { assert(!Conditions.empty() && "Trying to pop a condition from a condition-less group"); std::unique_ptr P = std::move(Conditions.front()); Conditions.erase(Conditions.begin()); return P; } const PredicateMatcher &getFirstCondition() const override { assert(!Conditions.empty() && "Trying to get a condition from a condition-less group"); return *Conditions.front(); } bool hasFirstCondition() const override { return !Conditions.empty(); } private: /// See if a candidate matcher could be added to this group solely by /// analyzing its first condition. bool candidateConditionMatches(const PredicateMatcher &Predicate) const; }; class SwitchMatcher : public Matcher { /// All the nested matchers, representing distinct switch-cases. The first /// conditions (as Matcher::getFirstCondition() reports) of all the nested /// matchers must share the same type and path to a value they check, in other /// words, be isIdenticalDownToValue, but have different values they check /// against. std::vector Matchers; /// The representative condition, with a type and a path (InsnVarID and OpIdx /// in most cases) shared by all the matchers contained. std::unique_ptr Condition = nullptr; /// Temporary set used to check that the case values don't repeat within the /// same switch. std::set Values; /// An owning collection for any auxiliary matchers created while optimizing /// nested matchers contained. std::vector> MatcherStorage; public: bool addMatcher(Matcher &Candidate); void finalize(); void emit(MatchTable &Table) override; iterator_range::iterator> matchers() { return make_range(Matchers.begin(), Matchers.end()); } size_t size() const { return Matchers.size(); } bool empty() const { return Matchers.empty(); } std::unique_ptr popFirstCondition() override { // SwitchMatcher doesn't have a common first condition for its cases, as all // the cases only share a kind of a value (a type and a path to it) they // match, but deliberately differ in the actual value they match. llvm_unreachable("Trying to pop a condition from a condition-less group"); } const PredicateMatcher &getFirstCondition() const override { llvm_unreachable("Trying to pop a condition from a condition-less group"); } bool hasFirstCondition() const override { return false; } private: /// See if the predicate type has a Switch-implementation for it. static bool isSupportedPredicateType(const PredicateMatcher &Predicate); bool candidateConditionMatches(const PredicateMatcher &Predicate) const; /// emit()-helper static void emitPredicateSpecificOpcodes(const PredicateMatcher &P, MatchTable &Table); }; /// Generates code to check that a match rule matches. class RuleMatcher : public Matcher { public: using ActionList = std::list>; using action_iterator = ActionList::iterator; protected: /// A list of matchers that all need to succeed for the current rule to match. /// FIXME: This currently supports a single match position but could be /// extended to support multiple positions to support div/rem fusion or /// load-multiple instructions. using MatchersTy = std::vector>; MatchersTy Matchers; /// A list of actions that need to be taken when all predicates in this rule /// have succeeded. ActionList Actions; /// Combiners can sometimes just run C++ code to finish matching a rule & /// mutate instructions instead of relying on MatchActions. Empty if unused. std::string CustomCXXAction; using DefinedInsnVariablesMap = std::map; /// A map of instruction matchers to the local variables DefinedInsnVariablesMap InsnVariableIDs; using MutatableInsnSet = SmallPtrSet; // The set of instruction matchers that have not yet been claimed for mutation // by a BuildMI. MutatableInsnSet MutatableInsns; /// A map of named operands defined by the matchers that may be referenced by /// the renderers. StringMap DefinedOperands; /// A map of anonymous physical register operands defined by the matchers that /// may be referenced by the renderers. DenseMap PhysRegOperands; /// ID for the next instruction variable defined with /// implicitlyDefineInsnVar() unsigned NextInsnVarID; /// ID for the next output instruction allocated with allocateOutputInsnID() unsigned NextOutputInsnID; /// ID for the next temporary register ID allocated with allocateTempRegID() unsigned NextTempRegID; /// ID for the next recorded type. Starts at -1 and counts down. TempTypeIdx NextTempTypeIdx = -1; // HwMode predicate index for this rule. -1 if no HwMode. int HwModeIdx = -1; /// Current GISelFlags GISelFlags Flags = 0; std::vector RequiredSimplePredicates; std::vector RequiredFeatures; std::vector> EpilogueMatchers; DenseSet ErasedInsnIDs; ArrayRef SrcLoc; typedef std::tuple DefinedComplexPatternSubOperand; typedef StringMap DefinedComplexPatternSubOperandMap; /// A map of Symbolic Names to ComplexPattern sub-operands. DefinedComplexPatternSubOperandMap ComplexSubOperands; /// A map used to for multiple referenced error check of ComplexSubOperand. /// ComplexSubOperand can't be referenced multiple from different operands, /// however multiple references from same operand are allowed since that is /// how 'same operand checks' are generated. StringMap ComplexSubOperandsParentName; uint64_t RuleID; static uint64_t NextRuleID; GISelFlags updateGISelFlag(GISelFlags CurFlags, const Record *R, StringRef FlagName, GISelFlags FlagBit); public: RuleMatcher(ArrayRef SrcLoc) : NextInsnVarID(0), NextOutputInsnID(0), NextTempRegID(0), SrcLoc(SrcLoc), RuleID(NextRuleID++) {} RuleMatcher(RuleMatcher &&Other) = default; RuleMatcher &operator=(RuleMatcher &&Other) = default; TempTypeIdx getNextTempTypeIdx() { return NextTempTypeIdx--; } uint64_t getRuleID() const { return RuleID; } InstructionMatcher &addInstructionMatcher(StringRef SymbolicName); void addRequiredFeature(Record *Feature); const std::vector &getRequiredFeatures() const; void addHwModeIdx(unsigned Idx) { HwModeIdx = Idx; } int getHwModeIdx() const { return HwModeIdx; } void addRequiredSimplePredicate(StringRef PredName); const std::vector &getRequiredSimplePredicates(); /// Attempts to mark \p ID as erased (GIR_EraseFromParent called on it). /// If \p ID has already been erased, returns false and GIR_EraseFromParent /// should NOT be emitted. bool tryEraseInsnID(unsigned ID) { return ErasedInsnIDs.insert(ID).second; } void setCustomCXXAction(StringRef FnEnumName) { CustomCXXAction = FnEnumName.str(); } // Emplaces an action of the specified Kind at the end of the action list. // // Returns a reference to the newly created action. // // Like std::vector::emplace_back(), may invalidate all iterators if the new // size exceeds the capacity. Otherwise, only invalidates the past-the-end // iterator. template Kind &addAction(Args &&...args) { Actions.emplace_back(std::make_unique(std::forward(args)...)); return *static_cast(Actions.back().get()); } // Emplaces an action of the specified Kind before the given insertion point. // // Returns an iterator pointing at the newly created instruction. // // Like std::vector::insert(), may invalidate all iterators if the new size // exceeds the capacity. Otherwise, only invalidates the iterators from the // insertion point onwards. template action_iterator insertAction(action_iterator InsertPt, Args &&...args) { return Actions.emplace(InsertPt, std::make_unique(std::forward(args)...)); } void setPermanentGISelFlags(GISelFlags V) { Flags = V; } // Update the active GISelFlags based on the GISelFlags Record R. // A SaveAndRestore object is returned so the old GISelFlags are restored // at the end of the scope. SaveAndRestore setGISelFlags(const Record *R); GISelFlags getGISelFlags() const { return Flags; } /// Define an instruction without emitting any code to do so. unsigned implicitlyDefineInsnVar(InstructionMatcher &Matcher); unsigned getInsnVarID(InstructionMatcher &InsnMatcher) const; DefinedInsnVariablesMap::const_iterator defined_insn_vars_begin() const { return InsnVariableIDs.begin(); } DefinedInsnVariablesMap::const_iterator defined_insn_vars_end() const { return InsnVariableIDs.end(); } iterator_range defined_insn_vars() const { return make_range(defined_insn_vars_begin(), defined_insn_vars_end()); } MutatableInsnSet::const_iterator mutatable_insns_begin() const { return MutatableInsns.begin(); } MutatableInsnSet::const_iterator mutatable_insns_end() const { return MutatableInsns.end(); } iterator_range mutatable_insns() const { return make_range(mutatable_insns_begin(), mutatable_insns_end()); } void reserveInsnMatcherForMutation(InstructionMatcher *InsnMatcher) { bool R = MutatableInsns.erase(InsnMatcher); assert(R && "Reserving a mutatable insn that isn't available"); (void)R; } action_iterator actions_begin() { return Actions.begin(); } action_iterator actions_end() { return Actions.end(); } iterator_range actions() { return make_range(actions_begin(), actions_end()); } void defineOperand(StringRef SymbolicName, OperandMatcher &OM); void definePhysRegOperand(Record *Reg, OperandMatcher &OM); Error defineComplexSubOperand(StringRef SymbolicName, Record *ComplexPattern, unsigned RendererID, unsigned SubOperandID, StringRef ParentSymbolicName); std::optional getComplexSubOperand(StringRef SymbolicName) const { const auto &I = ComplexSubOperands.find(SymbolicName); if (I == ComplexSubOperands.end()) return std::nullopt; return I->second; } InstructionMatcher &getInstructionMatcher(StringRef SymbolicName) const; OperandMatcher &getOperandMatcher(StringRef Name); const OperandMatcher &getOperandMatcher(StringRef Name) const; const OperandMatcher &getPhysRegOperandMatcher(Record *) const; void optimize() override; void emit(MatchTable &Table) override; /// Compare the priority of this object and B. /// /// Returns true if this object is more important than B. bool isHigherPriorityThan(const RuleMatcher &B) const; /// Report the maximum number of temporary operands needed by the rule /// matcher. unsigned countRendererFns() const; std::unique_ptr popFirstCondition() override; const PredicateMatcher &getFirstCondition() const override; LLTCodeGen getFirstConditionAsRootType(); bool hasFirstCondition() const override; unsigned getNumOperands() const; StringRef getOpcode() const; // FIXME: Remove this as soon as possible InstructionMatcher &insnmatchers_front() const { return *Matchers.front(); } unsigned allocateOutputInsnID() { return NextOutputInsnID++; } unsigned allocateTempRegID() { return NextTempRegID++; } iterator_range insnmatchers() { return make_range(Matchers.begin(), Matchers.end()); } bool insnmatchers_empty() const { return Matchers.empty(); } void insnmatchers_pop_front() { Matchers.erase(Matchers.begin()); } }; template class PredicateListMatcher { private: /// Template instantiations should specialize this to return a string to use /// for the comment emitted when there are no predicates. std::string getNoPredicateComment() const; protected: using PredicatesTy = std::deque>; PredicatesTy Predicates; /// Track if the list of predicates was manipulated by one of the optimization /// methods. bool Optimized = false; public: typename PredicatesTy::iterator predicates_begin() { return Predicates.begin(); } typename PredicatesTy::iterator predicates_end() { return Predicates.end(); } iterator_range predicates() { return make_range(predicates_begin(), predicates_end()); } typename PredicatesTy::size_type predicates_size() const { return Predicates.size(); } bool predicates_empty() const { return Predicates.empty(); } template bool contains() const { return any_of(Predicates, [&](auto &P) { return isa(P.get()); }); } std::unique_ptr predicates_pop_front() { std::unique_ptr Front = std::move(Predicates.front()); Predicates.pop_front(); Optimized = true; return Front; } void prependPredicate(std::unique_ptr &&Predicate) { Predicates.push_front(std::move(Predicate)); } void eraseNullPredicates() { const auto NewEnd = std::stable_partition(Predicates.begin(), Predicates.end(), std::logical_not>()); if (NewEnd != Predicates.begin()) { Predicates.erase(Predicates.begin(), NewEnd); Optimized = true; } } /// Emit MatchTable opcodes that tests whether all the predicates are met. template void emitPredicateListOpcodes(MatchTable &Table, Args &&...args) { if (Predicates.empty() && !Optimized) { Table << MatchTable::Comment(getNoPredicateComment()) << MatchTable::LineBreak; return; } for (const auto &Predicate : predicates()) Predicate->emitPredicateOpcodes(Table, std::forward(args)...); } /// Provide a function to avoid emitting certain predicates. This is used to /// defer some predicate checks until after others using PredicateFilterFunc = std::function; /// Emit MatchTable opcodes for predicates which satisfy \p /// ShouldEmitPredicate. This should be called multiple times to ensure all /// predicates are eventually added to the match table. template void emitFilteredPredicateListOpcodes(PredicateFilterFunc ShouldEmitPredicate, MatchTable &Table, Args &&...args) { if (Predicates.empty() && !Optimized) { Table << MatchTable::Comment(getNoPredicateComment()) << MatchTable::LineBreak; return; } for (const auto &Predicate : predicates()) { if (ShouldEmitPredicate(*Predicate)) Predicate->emitPredicateOpcodes(Table, std::forward(args)...); } } }; class PredicateMatcher { public: /// This enum is used for RTTI and also defines the priority that is given to /// the predicate when generating the matcher code. Kinds with higher priority /// must be tested first. /// /// The relative priority of OPM_LLT, OPM_RegBank, and OPM_MBB do not matter /// but OPM_Int must have priority over OPM_RegBank since constant integers /// are represented by a virtual register defined by a G_CONSTANT instruction. /// /// Note: The relative priority between IPM_ and OPM_ does not matter, they /// are currently not compared between each other. enum PredicateKind { IPM_Opcode, IPM_NumOperands, IPM_ImmPredicate, IPM_Imm, IPM_AtomicOrderingMMO, IPM_MemoryLLTSize, IPM_MemoryVsLLTSize, IPM_MemoryAddressSpace, IPM_MemoryAlignment, IPM_VectorSplatImm, IPM_NoUse, IPM_OneUse, IPM_GenericPredicate, IPM_MIFlags, OPM_SameOperand, OPM_ComplexPattern, OPM_IntrinsicID, OPM_CmpPredicate, OPM_Instruction, OPM_Int, OPM_LiteralInt, OPM_LLT, OPM_PointerToAny, OPM_RegBank, OPM_MBB, OPM_RecordNamedOperand, OPM_RecordRegType, }; protected: PredicateKind Kind; unsigned InsnVarID; unsigned OpIdx; public: PredicateMatcher(PredicateKind Kind, unsigned InsnVarID, unsigned OpIdx = ~0) : Kind(Kind), InsnVarID(InsnVarID), OpIdx(OpIdx) {} virtual ~PredicateMatcher(); unsigned getInsnVarID() const { return InsnVarID; } unsigned getOpIdx() const { return OpIdx; } /// Emit MatchTable opcodes that check the predicate for the given operand. virtual void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const = 0; PredicateKind getKind() const { return Kind; } bool dependsOnOperands() const { // Custom predicates really depend on the context pattern of the // instruction, not just the individual instruction. This therefore // implicitly depends on all other pattern constraints. return Kind == IPM_GenericPredicate; } virtual bool isIdentical(const PredicateMatcher &B) const { return B.getKind() == getKind() && InsnVarID == B.InsnVarID && OpIdx == B.OpIdx; } virtual bool isIdenticalDownToValue(const PredicateMatcher &B) const { return hasValue() && PredicateMatcher::isIdentical(B); } virtual MatchTableRecord getValue() const { assert(hasValue() && "Can not get a value of a value-less predicate!"); llvm_unreachable("Not implemented yet"); } virtual bool hasValue() const { return false; } /// Report the maximum number of temporary operands needed by the predicate /// matcher. virtual unsigned countRendererFns() const { return 0; } }; /// Generates code to check a predicate of an operand. /// /// Typical predicates include: /// * Operand is a particular register. /// * Operand is assigned a particular register bank. /// * Operand is an MBB. class OperandPredicateMatcher : public PredicateMatcher { public: OperandPredicateMatcher(PredicateKind Kind, unsigned InsnVarID, unsigned OpIdx) : PredicateMatcher(Kind, InsnVarID, OpIdx) {} virtual ~OperandPredicateMatcher(); /// Compare the priority of this object and B. /// /// Returns true if this object is more important than B. virtual bool isHigherPriorityThan(const OperandPredicateMatcher &B) const; }; template <> inline std::string PredicateListMatcher::getNoPredicateComment() const { return "No operand predicates"; } /// Generates code to check that a register operand is defined by the same exact /// one as another. class SameOperandMatcher : public OperandPredicateMatcher { std::string MatchingName; unsigned OrigOpIdx; GISelFlags Flags; public: SameOperandMatcher(unsigned InsnVarID, unsigned OpIdx, StringRef MatchingName, unsigned OrigOpIdx, GISelFlags Flags) : OperandPredicateMatcher(OPM_SameOperand, InsnVarID, OpIdx), MatchingName(MatchingName), OrigOpIdx(OrigOpIdx), Flags(Flags) {} static bool classof(const PredicateMatcher *P) { return P->getKind() == OPM_SameOperand; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; bool isIdentical(const PredicateMatcher &B) const override { return OperandPredicateMatcher::isIdentical(B) && OrigOpIdx == cast(&B)->OrigOpIdx && MatchingName == cast(&B)->MatchingName; } }; /// Generates code to check that an operand is a particular LLT. class LLTOperandMatcher : public OperandPredicateMatcher { protected: LLTCodeGen Ty; public: static std::map TypeIDValues; static void initTypeIDValuesMap() { TypeIDValues.clear(); unsigned ID = 0; for (const LLTCodeGen &LLTy : KnownTypes) TypeIDValues[LLTy] = ID++; } LLTOperandMatcher(unsigned InsnVarID, unsigned OpIdx, const LLTCodeGen &Ty) : OperandPredicateMatcher(OPM_LLT, InsnVarID, OpIdx), Ty(Ty) { KnownTypes.insert(Ty); } static bool classof(const PredicateMatcher *P) { return P->getKind() == OPM_LLT; } bool isIdentical(const PredicateMatcher &B) const override { return OperandPredicateMatcher::isIdentical(B) && Ty == cast(&B)->Ty; } MatchTableRecord getValue() const override; bool hasValue() const override; LLTCodeGen getTy() const { return Ty; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to check that an operand is a pointer to any address space. /// /// In SelectionDAG, the types did not describe pointers or address spaces. As a /// result, iN is used to describe a pointer of N bits to any address space and /// PatFrag predicates are typically used to constrain the address space. /// There's no reliable means to derive the missing type information from the /// pattern so imported rules must test the components of a pointer separately. /// /// If SizeInBits is zero, then the pointer size will be obtained from the /// subtarget. class PointerToAnyOperandMatcher : public OperandPredicateMatcher { protected: unsigned SizeInBits; public: PointerToAnyOperandMatcher(unsigned InsnVarID, unsigned OpIdx, unsigned SizeInBits) : OperandPredicateMatcher(OPM_PointerToAny, InsnVarID, OpIdx), SizeInBits(SizeInBits) {} static bool classof(const PredicateMatcher *P) { return P->getKind() == OPM_PointerToAny; } bool isIdentical(const PredicateMatcher &B) const override { return OperandPredicateMatcher::isIdentical(B) && SizeInBits == cast(&B)->SizeInBits; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to record named operand in RecordedOperands list at StoreIdx. /// Predicates with 'let PredicateCodeUsesOperands = 1' get RecordedOperands as /// an argument to predicate's c++ code once all operands have been matched. class RecordNamedOperandMatcher : public OperandPredicateMatcher { protected: unsigned StoreIdx; std::string Name; public: RecordNamedOperandMatcher(unsigned InsnVarID, unsigned OpIdx, unsigned StoreIdx, StringRef Name) : OperandPredicateMatcher(OPM_RecordNamedOperand, InsnVarID, OpIdx), StoreIdx(StoreIdx), Name(Name) {} static bool classof(const PredicateMatcher *P) { return P->getKind() == OPM_RecordNamedOperand; } bool isIdentical(const PredicateMatcher &B) const override { return OperandPredicateMatcher::isIdentical(B) && StoreIdx == cast(&B)->StoreIdx && Name == cast(&B)->Name; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to store a register operand's type into the set of temporary /// LLTs. class RecordRegisterType : public OperandPredicateMatcher { protected: TempTypeIdx Idx; public: RecordRegisterType(unsigned InsnVarID, unsigned OpIdx, TempTypeIdx Idx) : OperandPredicateMatcher(OPM_RecordRegType, InsnVarID, OpIdx), Idx(Idx) { } static bool classof(const PredicateMatcher *P) { return P->getKind() == OPM_RecordRegType; } bool isIdentical(const PredicateMatcher &B) const override { return OperandPredicateMatcher::isIdentical(B) && Idx == cast(&B)->Idx; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to check that an operand is a particular target constant. class ComplexPatternOperandMatcher : public OperandPredicateMatcher { protected: const OperandMatcher &Operand; const Record &TheDef; unsigned getAllocatedTemporariesBaseID() const; public: bool isIdentical(const PredicateMatcher &B) const override { return false; } ComplexPatternOperandMatcher(unsigned InsnVarID, unsigned OpIdx, const OperandMatcher &Operand, const Record &TheDef) : OperandPredicateMatcher(OPM_ComplexPattern, InsnVarID, OpIdx), Operand(Operand), TheDef(TheDef) {} static bool classof(const PredicateMatcher *P) { return P->getKind() == OPM_ComplexPattern; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; unsigned countRendererFns() const override { return 1; } }; /// Generates code to check that an operand is in a particular register bank. class RegisterBankOperandMatcher : public OperandPredicateMatcher { protected: const CodeGenRegisterClass &RC; public: RegisterBankOperandMatcher(unsigned InsnVarID, unsigned OpIdx, const CodeGenRegisterClass &RC) : OperandPredicateMatcher(OPM_RegBank, InsnVarID, OpIdx), RC(RC) {} bool isIdentical(const PredicateMatcher &B) const override; static bool classof(const PredicateMatcher *P) { return P->getKind() == OPM_RegBank; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to check that an operand is a basic block. class MBBOperandMatcher : public OperandPredicateMatcher { public: MBBOperandMatcher(unsigned InsnVarID, unsigned OpIdx) : OperandPredicateMatcher(OPM_MBB, InsnVarID, OpIdx) {} static bool classof(const PredicateMatcher *P) { return P->getKind() == OPM_MBB; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; class ImmOperandMatcher : public OperandPredicateMatcher { public: ImmOperandMatcher(unsigned InsnVarID, unsigned OpIdx) : OperandPredicateMatcher(IPM_Imm, InsnVarID, OpIdx) {} static bool classof(const PredicateMatcher *P) { return P->getKind() == IPM_Imm; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to check that an operand is a G_CONSTANT with a particular /// int. class ConstantIntOperandMatcher : public OperandPredicateMatcher { protected: int64_t Value; public: ConstantIntOperandMatcher(unsigned InsnVarID, unsigned OpIdx, int64_t Value) : OperandPredicateMatcher(OPM_Int, InsnVarID, OpIdx), Value(Value) {} bool isIdentical(const PredicateMatcher &B) const override { return OperandPredicateMatcher::isIdentical(B) && Value == cast(&B)->Value; } static bool classof(const PredicateMatcher *P) { return P->getKind() == OPM_Int; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to check that an operand is a raw int (where MO.isImm() or /// MO.isCImm() is true). class LiteralIntOperandMatcher : public OperandPredicateMatcher { protected: int64_t Value; public: LiteralIntOperandMatcher(unsigned InsnVarID, unsigned OpIdx, int64_t Value) : OperandPredicateMatcher(OPM_LiteralInt, InsnVarID, OpIdx), Value(Value) {} bool isIdentical(const PredicateMatcher &B) const override { return OperandPredicateMatcher::isIdentical(B) && Value == cast(&B)->Value; } static bool classof(const PredicateMatcher *P) { return P->getKind() == OPM_LiteralInt; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to check that an operand is an CmpInst predicate class CmpPredicateOperandMatcher : public OperandPredicateMatcher { protected: std::string PredName; public: CmpPredicateOperandMatcher(unsigned InsnVarID, unsigned OpIdx, std::string P) : OperandPredicateMatcher(OPM_CmpPredicate, InsnVarID, OpIdx), PredName(std::move(P)) {} bool isIdentical(const PredicateMatcher &B) const override { return OperandPredicateMatcher::isIdentical(B) && PredName == cast(&B)->PredName; } static bool classof(const PredicateMatcher *P) { return P->getKind() == OPM_CmpPredicate; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to check that an operand is an intrinsic ID. class IntrinsicIDOperandMatcher : public OperandPredicateMatcher { protected: const CodeGenIntrinsic *II; public: IntrinsicIDOperandMatcher(unsigned InsnVarID, unsigned OpIdx, const CodeGenIntrinsic *II) : OperandPredicateMatcher(OPM_IntrinsicID, InsnVarID, OpIdx), II(II) {} bool isIdentical(const PredicateMatcher &B) const override { return OperandPredicateMatcher::isIdentical(B) && II == cast(&B)->II; } static bool classof(const PredicateMatcher *P) { return P->getKind() == OPM_IntrinsicID; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to check that this operand is an immediate whose value meets /// an immediate predicate. class OperandImmPredicateMatcher : public OperandPredicateMatcher { protected: TreePredicateFn Predicate; public: OperandImmPredicateMatcher(unsigned InsnVarID, unsigned OpIdx, const TreePredicateFn &Predicate) : OperandPredicateMatcher(IPM_ImmPredicate, InsnVarID, OpIdx), Predicate(Predicate) {} bool isIdentical(const PredicateMatcher &B) const override { return OperandPredicateMatcher::isIdentical(B) && Predicate.getOrigPatFragRecord() == cast(&B) ->Predicate.getOrigPatFragRecord(); } static bool classof(const PredicateMatcher *P) { return P->getKind() == IPM_ImmPredicate; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to check that a set of predicates match for a particular /// operand. class OperandMatcher : public PredicateListMatcher { protected: InstructionMatcher &Insn; unsigned OpIdx; std::string SymbolicName; /// The index of the first temporary variable allocated to this operand. The /// number of allocated temporaries can be found with /// countRendererFns(). unsigned AllocatedTemporariesBaseID; TempTypeIdx TTIdx = 0; public: OperandMatcher(InstructionMatcher &Insn, unsigned OpIdx, const std::string &SymbolicName, unsigned AllocatedTemporariesBaseID) : Insn(Insn), OpIdx(OpIdx), SymbolicName(SymbolicName), AllocatedTemporariesBaseID(AllocatedTemporariesBaseID) {} bool hasSymbolicName() const { return !SymbolicName.empty(); } StringRef getSymbolicName() const { return SymbolicName; } void setSymbolicName(StringRef Name) { assert(SymbolicName.empty() && "Operand already has a symbolic name"); SymbolicName = std::string(Name); } /// Construct a new operand predicate and add it to the matcher. template std::optional addPredicate(Args &&...args) { if (isSameAsAnotherOperand()) return std::nullopt; Predicates.emplace_back(std::make_unique( getInsnVarID(), getOpIdx(), std::forward(args)...)); return static_cast(Predicates.back().get()); } unsigned getOpIdx() const { return OpIdx; } unsigned getInsnVarID() const; /// If this OperandMatcher has not been assigned a TempTypeIdx yet, assigns it /// one and adds a `RecordRegisterType` predicate to this matcher. If one has /// already been assigned, simply returns it. TempTypeIdx getTempTypeIdx(RuleMatcher &Rule); std::string getOperandExpr(unsigned InsnVarID) const; InstructionMatcher &getInstructionMatcher() const { return Insn; } Error addTypeCheckPredicate(const TypeSetByHwMode &VTy, bool OperandIsAPointer); /// Emit MatchTable opcodes that test whether the instruction named in /// InsnVarID matches all the predicates and all the operands. void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule); /// Compare the priority of this object and B. /// /// Returns true if this object is more important than B. bool isHigherPriorityThan(OperandMatcher &B); /// Report the maximum number of temporary operands needed by the operand /// matcher. unsigned countRendererFns(); unsigned getAllocatedTemporariesBaseID() const { return AllocatedTemporariesBaseID; } bool isSameAsAnotherOperand() { for (const auto &Predicate : predicates()) if (isa(Predicate)) return true; return false; } }; /// Generates code to check a predicate on an instruction. /// /// Typical predicates include: /// * The opcode of the instruction is a particular value. /// * The nsw/nuw flag is/isn't set. class InstructionPredicateMatcher : public PredicateMatcher { public: InstructionPredicateMatcher(PredicateKind Kind, unsigned InsnVarID) : PredicateMatcher(Kind, InsnVarID) {} virtual ~InstructionPredicateMatcher() {} /// Compare the priority of this object and B. /// /// Returns true if this object is more important than B. virtual bool isHigherPriorityThan(const InstructionPredicateMatcher &B) const { return Kind < B.Kind; }; }; template <> inline std::string PredicateListMatcher::getNoPredicateComment() const { return "No instruction predicates"; } /// Generates code to check the opcode of an instruction. class InstructionOpcodeMatcher : public InstructionPredicateMatcher { protected: // Allow matching one to several, similar opcodes that share properties. This // is to handle patterns where one SelectionDAG operation maps to multiple // GlobalISel ones (e.g. G_BUILD_VECTOR and G_BUILD_VECTOR_TRUNC). The first // is treated as the canonical opcode. SmallVector Insts; static DenseMap OpcodeValues; MatchTableRecord getInstValue(const CodeGenInstruction *I) const; public: static void initOpcodeValuesMap(const CodeGenTarget &Target); InstructionOpcodeMatcher(unsigned InsnVarID, ArrayRef I) : InstructionPredicateMatcher(IPM_Opcode, InsnVarID), Insts(I.begin(), I.end()) { assert((Insts.size() == 1 || Insts.size() == 2) && "unexpected number of opcode alternatives"); } static bool classof(const PredicateMatcher *P) { return P->getKind() == IPM_Opcode; } bool isIdentical(const PredicateMatcher &B) const override { return InstructionPredicateMatcher::isIdentical(B) && Insts == cast(&B)->Insts; } bool hasValue() const override { return Insts.size() == 1 && OpcodeValues.count(Insts[0]); } // TODO: This is used for the SwitchMatcher optimization. We should be able to // return a list of the opcodes to match. MatchTableRecord getValue() const override; void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; /// Compare the priority of this object and B. /// /// Returns true if this object is more important than B. bool isHigherPriorityThan(const InstructionPredicateMatcher &B) const override; bool isConstantInstruction() const; // The first opcode is the canonical opcode, and later are alternatives. StringRef getOpcode() const; ArrayRef getAlternativeOpcodes() { return Insts; } bool isVariadicNumOperands() const; StringRef getOperandType(unsigned OpIdx) const; }; class InstructionNumOperandsMatcher final : public InstructionPredicateMatcher { unsigned NumOperands = 0; public: InstructionNumOperandsMatcher(unsigned InsnVarID, unsigned NumOperands) : InstructionPredicateMatcher(IPM_NumOperands, InsnVarID), NumOperands(NumOperands) {} static bool classof(const PredicateMatcher *P) { return P->getKind() == IPM_NumOperands; } bool isIdentical(const PredicateMatcher &B) const override { return InstructionPredicateMatcher::isIdentical(B) && NumOperands == cast(&B)->NumOperands; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to check that this instruction is a constant whose value /// meets an immediate predicate. /// /// Immediates are slightly odd since they are typically used like an operand /// but are represented as an operator internally. We typically write simm8:$src /// in a tablegen pattern, but this is just syntactic sugar for /// (imm:i32)<>:$imm which more directly describes the nodes /// that will be matched and the predicate (which is attached to the imm /// operator) that will be tested. In SelectionDAG this describes a /// ConstantSDNode whose internal value will be tested using the simm8 /// predicate. /// /// The corresponding GlobalISel representation is %1 = G_CONSTANT iN Value. In /// this representation, the immediate could be tested with an /// InstructionMatcher, InstructionOpcodeMatcher, OperandMatcher, and a /// OperandPredicateMatcher-subclass to check the Value meets the predicate but /// there are two implementation issues with producing that matcher /// configuration from the SelectionDAG pattern: /// * ImmLeaf is a PatFrag whose root is an InstructionMatcher. This means that /// were we to sink the immediate predicate to the operand we would have to /// have two partial implementations of PatFrag support, one for immediates /// and one for non-immediates. /// * At the point we handle the predicate, the OperandMatcher hasn't been /// created yet. If we were to sink the predicate to the OperandMatcher we /// would also have to complicate (or duplicate) the code that descends and /// creates matchers for the subtree. /// Overall, it's simpler to handle it in the place it was found. class InstructionImmPredicateMatcher : public InstructionPredicateMatcher { protected: TreePredicateFn Predicate; public: InstructionImmPredicateMatcher(unsigned InsnVarID, const TreePredicateFn &Predicate) : InstructionPredicateMatcher(IPM_ImmPredicate, InsnVarID), Predicate(Predicate) {} bool isIdentical(const PredicateMatcher &B) const override; static bool classof(const PredicateMatcher *P) { return P->getKind() == IPM_ImmPredicate; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to check that a memory instruction has a atomic ordering /// MachineMemoryOperand. class AtomicOrderingMMOPredicateMatcher : public InstructionPredicateMatcher { public: enum AOComparator { AO_Exactly, AO_OrStronger, AO_WeakerThan, }; protected: StringRef Order; AOComparator Comparator; public: AtomicOrderingMMOPredicateMatcher(unsigned InsnVarID, StringRef Order, AOComparator Comparator = AO_Exactly) : InstructionPredicateMatcher(IPM_AtomicOrderingMMO, InsnVarID), Order(Order), Comparator(Comparator) {} static bool classof(const PredicateMatcher *P) { return P->getKind() == IPM_AtomicOrderingMMO; } bool isIdentical(const PredicateMatcher &B) const override; void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to check that the size of an MMO is exactly N bytes. class MemorySizePredicateMatcher : public InstructionPredicateMatcher { protected: unsigned MMOIdx; uint64_t Size; public: MemorySizePredicateMatcher(unsigned InsnVarID, unsigned MMOIdx, unsigned Size) : InstructionPredicateMatcher(IPM_MemoryLLTSize, InsnVarID), MMOIdx(MMOIdx), Size(Size) {} static bool classof(const PredicateMatcher *P) { return P->getKind() == IPM_MemoryLLTSize; } bool isIdentical(const PredicateMatcher &B) const override { return InstructionPredicateMatcher::isIdentical(B) && MMOIdx == cast(&B)->MMOIdx && Size == cast(&B)->Size; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; class MemoryAddressSpacePredicateMatcher : public InstructionPredicateMatcher { protected: unsigned MMOIdx; SmallVector AddrSpaces; public: MemoryAddressSpacePredicateMatcher(unsigned InsnVarID, unsigned MMOIdx, ArrayRef AddrSpaces) : InstructionPredicateMatcher(IPM_MemoryAddressSpace, InsnVarID), MMOIdx(MMOIdx), AddrSpaces(AddrSpaces.begin(), AddrSpaces.end()) {} static bool classof(const PredicateMatcher *P) { return P->getKind() == IPM_MemoryAddressSpace; } bool isIdentical(const PredicateMatcher &B) const override; void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; class MemoryAlignmentPredicateMatcher : public InstructionPredicateMatcher { protected: unsigned MMOIdx; int MinAlign; public: MemoryAlignmentPredicateMatcher(unsigned InsnVarID, unsigned MMOIdx, int MinAlign) : InstructionPredicateMatcher(IPM_MemoryAlignment, InsnVarID), MMOIdx(MMOIdx), MinAlign(MinAlign) { assert(MinAlign > 0); } static bool classof(const PredicateMatcher *P) { return P->getKind() == IPM_MemoryAlignment; } bool isIdentical(const PredicateMatcher &B) const override; void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to check that the size of an MMO is less-than, equal-to, or /// greater than a given LLT. class MemoryVsLLTSizePredicateMatcher : public InstructionPredicateMatcher { public: enum RelationKind { GreaterThan, EqualTo, LessThan, }; protected: unsigned MMOIdx; RelationKind Relation; unsigned OpIdx; public: MemoryVsLLTSizePredicateMatcher(unsigned InsnVarID, unsigned MMOIdx, enum RelationKind Relation, unsigned OpIdx) : InstructionPredicateMatcher(IPM_MemoryVsLLTSize, InsnVarID), MMOIdx(MMOIdx), Relation(Relation), OpIdx(OpIdx) {} static bool classof(const PredicateMatcher *P) { return P->getKind() == IPM_MemoryVsLLTSize; } bool isIdentical(const PredicateMatcher &B) const override; void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; // Matcher for immAllOnesV/immAllZerosV class VectorSplatImmPredicateMatcher : public InstructionPredicateMatcher { public: enum SplatKind { AllZeros, AllOnes }; private: SplatKind Kind; public: VectorSplatImmPredicateMatcher(unsigned InsnVarID, SplatKind K) : InstructionPredicateMatcher(IPM_VectorSplatImm, InsnVarID), Kind(K) {} static bool classof(const PredicateMatcher *P) { return P->getKind() == IPM_VectorSplatImm; } bool isIdentical(const PredicateMatcher &B) const override { return InstructionPredicateMatcher::isIdentical(B) && Kind == static_cast(B).Kind; } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to check an arbitrary C++ instruction predicate. class GenericInstructionPredicateMatcher : public InstructionPredicateMatcher { protected: std::string EnumVal; public: GenericInstructionPredicateMatcher(unsigned InsnVarID, TreePredicateFn Predicate); GenericInstructionPredicateMatcher(unsigned InsnVarID, const std::string &EnumVal) : InstructionPredicateMatcher(IPM_GenericPredicate, InsnVarID), EnumVal(EnumVal) {} static bool classof(const InstructionPredicateMatcher *P) { return P->getKind() == IPM_GenericPredicate; } bool isIdentical(const PredicateMatcher &B) const override; void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; class MIFlagsInstructionPredicateMatcher : public InstructionPredicateMatcher { SmallVector Flags; bool CheckNot; // false = GIM_MIFlags, true = GIM_MIFlagsNot public: MIFlagsInstructionPredicateMatcher(unsigned InsnVarID, ArrayRef FlagsToCheck, bool CheckNot = false) : InstructionPredicateMatcher(IPM_MIFlags, InsnVarID), Flags(FlagsToCheck), CheckNot(CheckNot) { sort(Flags); } static bool classof(const InstructionPredicateMatcher *P) { return P->getKind() == IPM_MIFlags; } bool isIdentical(const PredicateMatcher &B) const override; void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to check for the absence of use of the result. // TODO? Generalize this to support checking for one use. class NoUsePredicateMatcher : public InstructionPredicateMatcher { public: NoUsePredicateMatcher(unsigned InsnVarID) : InstructionPredicateMatcher(IPM_NoUse, InsnVarID) {} static bool classof(const PredicateMatcher *P) { return P->getKind() == IPM_NoUse; } bool isIdentical(const PredicateMatcher &B) const override { return InstructionPredicateMatcher::isIdentical(B); } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override { Table << MatchTable::Opcode("GIM_CheckHasNoUse") << MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID) << MatchTable::LineBreak; } }; /// Generates code to check that the first result has only one use. class OneUsePredicateMatcher : public InstructionPredicateMatcher { public: OneUsePredicateMatcher(unsigned InsnVarID) : InstructionPredicateMatcher(IPM_OneUse, InsnVarID) {} static bool classof(const PredicateMatcher *P) { return P->getKind() == IPM_OneUse; } bool isIdentical(const PredicateMatcher &B) const override { return InstructionPredicateMatcher::isIdentical(B); } void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override { Table << MatchTable::Opcode("GIM_CheckHasOneUse") << MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID) << MatchTable::LineBreak; } }; /// Generates code to check that a set of predicates and operands match for a /// particular instruction. /// /// Typical predicates include: /// * Has a specific opcode. /// * Has an nsw/nuw flag or doesn't. class InstructionMatcher final : public PredicateListMatcher { protected: typedef std::vector> OperandVec; RuleMatcher &Rule; /// The operands to match. All rendered operands must be present even if the /// condition is always true. OperandVec Operands; bool NumOperandsCheck = true; std::string SymbolicName; unsigned InsnVarID; /// PhysRegInputs - List list has an entry for each explicitly specified /// physreg input to the pattern. The first elt is the Register node, the /// second is the recorded slot number the input pattern match saved it in. SmallVector, 2> PhysRegInputs; public: InstructionMatcher(RuleMatcher &Rule, StringRef SymbolicName, bool NumOpsCheck = true) : Rule(Rule), NumOperandsCheck(NumOpsCheck), SymbolicName(SymbolicName) { // We create a new instruction matcher. // Get a new ID for that instruction. InsnVarID = Rule.implicitlyDefineInsnVar(*this); } /// Construct a new instruction predicate and add it to the matcher. template std::optional addPredicate(Args &&...args) { Predicates.emplace_back( std::make_unique(getInsnVarID(), std::forward(args)...)); return static_cast(Predicates.back().get()); } RuleMatcher &getRuleMatcher() const { return Rule; } unsigned getInsnVarID() const { return InsnVarID; } /// Add an operand to the matcher. OperandMatcher &addOperand(unsigned OpIdx, const std::string &SymbolicName, unsigned AllocatedTemporariesBaseID); OperandMatcher &getOperand(unsigned OpIdx); OperandMatcher &addPhysRegInput(Record *Reg, unsigned OpIdx, unsigned TempOpIdx); ArrayRef> getPhysRegInputs() const { return PhysRegInputs; } StringRef getSymbolicName() const { return SymbolicName; } unsigned getNumOperands() const { return Operands.size(); } OperandVec::iterator operands_begin() { return Operands.begin(); } OperandVec::iterator operands_end() { return Operands.end(); } iterator_range operands() { return make_range(operands_begin(), operands_end()); } OperandVec::const_iterator operands_begin() const { return Operands.begin(); } OperandVec::const_iterator operands_end() const { return Operands.end(); } iterator_range operands() const { return make_range(operands_begin(), operands_end()); } bool operands_empty() const { return Operands.empty(); } void pop_front() { Operands.erase(Operands.begin()); } void optimize(); /// Emit MatchTable opcodes that test whether the instruction named in /// InsnVarName matches all the predicates and all the operands. void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule); /// Compare the priority of this object and B. /// /// Returns true if this object is more important than B. bool isHigherPriorityThan(InstructionMatcher &B); /// Report the maximum number of temporary operands needed by the instruction /// matcher. unsigned countRendererFns(); InstructionOpcodeMatcher &getOpcodeMatcher() { for (auto &P : predicates()) if (auto *OpMatcher = dyn_cast(P.get())) return *OpMatcher; llvm_unreachable("Didn't find an opcode matcher"); } bool isConstantInstruction() { return getOpcodeMatcher().isConstantInstruction(); } StringRef getOpcode() { return getOpcodeMatcher().getOpcode(); } }; /// Generates code to check that the operand is a register defined by an /// instruction that matches the given instruction matcher. /// /// For example, the pattern: /// (set $dst, (G_MUL (G_ADD $src1, $src2), $src3)) /// would use an InstructionOperandMatcher for operand 1 of the G_MUL to match /// the: /// (G_ADD $src1, $src2) /// subpattern. class InstructionOperandMatcher : public OperandPredicateMatcher { protected: std::unique_ptr InsnMatcher; GISelFlags Flags; public: InstructionOperandMatcher(unsigned InsnVarID, unsigned OpIdx, RuleMatcher &Rule, StringRef SymbolicName, bool NumOpsCheck = true) : OperandPredicateMatcher(OPM_Instruction, InsnVarID, OpIdx), InsnMatcher(new InstructionMatcher(Rule, SymbolicName, NumOpsCheck)), Flags(Rule.getGISelFlags()) {} static bool classof(const PredicateMatcher *P) { return P->getKind() == OPM_Instruction; } InstructionMatcher &getInsnMatcher() const { return *InsnMatcher; } void emitCaptureOpcodes(MatchTable &Table, RuleMatcher &Rule) const; void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) const override { emitCaptureOpcodes(Table, Rule); InsnMatcher->emitPredicateOpcodes(Table, Rule); } bool isHigherPriorityThan(const OperandPredicateMatcher &B) const override; /// Report the maximum number of temporary operands needed by the predicate /// matcher. unsigned countRendererFns() const override { return InsnMatcher->countRendererFns(); } }; //===- Actions ------------------------------------------------------------===// class OperandRenderer { public: enum RendererKind { OR_Copy, OR_CopyOrAddZeroReg, OR_CopySubReg, OR_CopyPhysReg, OR_CopyConstantAsImm, OR_CopyFConstantAsFPImm, OR_Imm, OR_SubRegIndex, OR_Register, OR_TempRegister, OR_ComplexPattern, OR_Intrinsic, OR_Custom, OR_CustomOperand }; protected: RendererKind Kind; public: OperandRenderer(RendererKind Kind) : Kind(Kind) {} virtual ~OperandRenderer(); RendererKind getKind() const { return Kind; } virtual void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const = 0; }; /// A CopyRenderer emits code to copy a single operand from an existing /// instruction to the one being built. class CopyRenderer : public OperandRenderer { protected: unsigned NewInsnID; /// The name of the operand. const StringRef SymbolicName; public: CopyRenderer(unsigned NewInsnID, StringRef SymbolicName) : OperandRenderer(OR_Copy), NewInsnID(NewInsnID), SymbolicName(SymbolicName) { assert(!SymbolicName.empty() && "Cannot copy from an unspecified source"); } static bool classof(const OperandRenderer *R) { return R->getKind() == OR_Copy; } StringRef getSymbolicName() const { return SymbolicName; } static void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule, unsigned NewInsnID, unsigned OldInsnID, unsigned OpIdx, StringRef Name); void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// A CopyRenderer emits code to copy a virtual register to a specific physical /// register. class CopyPhysRegRenderer : public OperandRenderer { protected: unsigned NewInsnID; Record *PhysReg; public: CopyPhysRegRenderer(unsigned NewInsnID, Record *Reg) : OperandRenderer(OR_CopyPhysReg), NewInsnID(NewInsnID), PhysReg(Reg) { assert(PhysReg); } static bool classof(const OperandRenderer *R) { return R->getKind() == OR_CopyPhysReg; } Record *getPhysReg() const { return PhysReg; } void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// A CopyOrAddZeroRegRenderer emits code to copy a single operand from an /// existing instruction to the one being built. If the operand turns out to be /// a 'G_CONSTANT 0' then it replaces the operand with a zero register. class CopyOrAddZeroRegRenderer : public OperandRenderer { protected: unsigned NewInsnID; /// The name of the operand. const StringRef SymbolicName; const Record *ZeroRegisterDef; public: CopyOrAddZeroRegRenderer(unsigned NewInsnID, StringRef SymbolicName, Record *ZeroRegisterDef) : OperandRenderer(OR_CopyOrAddZeroReg), NewInsnID(NewInsnID), SymbolicName(SymbolicName), ZeroRegisterDef(ZeroRegisterDef) { assert(!SymbolicName.empty() && "Cannot copy from an unspecified source"); } static bool classof(const OperandRenderer *R) { return R->getKind() == OR_CopyOrAddZeroReg; } StringRef getSymbolicName() const { return SymbolicName; } void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// A CopyConstantAsImmRenderer emits code to render a G_CONSTANT instruction to /// an extended immediate operand. class CopyConstantAsImmRenderer : public OperandRenderer { protected: unsigned NewInsnID; /// The name of the operand. const std::string SymbolicName; bool Signed; public: CopyConstantAsImmRenderer(unsigned NewInsnID, StringRef SymbolicName) : OperandRenderer(OR_CopyConstantAsImm), NewInsnID(NewInsnID), SymbolicName(SymbolicName), Signed(true) {} static bool classof(const OperandRenderer *R) { return R->getKind() == OR_CopyConstantAsImm; } StringRef getSymbolicName() const { return SymbolicName; } void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// A CopyFConstantAsFPImmRenderer emits code to render a G_FCONSTANT /// instruction to an extended immediate operand. class CopyFConstantAsFPImmRenderer : public OperandRenderer { protected: unsigned NewInsnID; /// The name of the operand. const std::string SymbolicName; public: CopyFConstantAsFPImmRenderer(unsigned NewInsnID, StringRef SymbolicName) : OperandRenderer(OR_CopyFConstantAsFPImm), NewInsnID(NewInsnID), SymbolicName(SymbolicName) {} static bool classof(const OperandRenderer *R) { return R->getKind() == OR_CopyFConstantAsFPImm; } StringRef getSymbolicName() const { return SymbolicName; } void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// A CopySubRegRenderer emits code to copy a single register operand from an /// existing instruction to the one being built and indicate that only a /// subregister should be copied. class CopySubRegRenderer : public OperandRenderer { protected: unsigned NewInsnID; /// The name of the operand. const StringRef SymbolicName; /// The subregister to extract. const CodeGenSubRegIndex *SubReg; public: CopySubRegRenderer(unsigned NewInsnID, StringRef SymbolicName, const CodeGenSubRegIndex *SubReg) : OperandRenderer(OR_CopySubReg), NewInsnID(NewInsnID), SymbolicName(SymbolicName), SubReg(SubReg) {} static bool classof(const OperandRenderer *R) { return R->getKind() == OR_CopySubReg; } StringRef getSymbolicName() const { return SymbolicName; } void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Adds a specific physical register to the instruction being built. /// This is typically useful for WZR/XZR on AArch64. class AddRegisterRenderer : public OperandRenderer { protected: unsigned InsnID; const Record *RegisterDef; bool IsDef; const CodeGenTarget &Target; public: AddRegisterRenderer(unsigned InsnID, const CodeGenTarget &Target, const Record *RegisterDef, bool IsDef = false) : OperandRenderer(OR_Register), InsnID(InsnID), RegisterDef(RegisterDef), IsDef(IsDef), Target(Target) {} static bool classof(const OperandRenderer *R) { return R->getKind() == OR_Register; } void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Adds a specific temporary virtual register to the instruction being built. /// This is used to chain instructions together when emitting multiple /// instructions. class TempRegRenderer : public OperandRenderer { protected: unsigned InsnID; unsigned TempRegID; const CodeGenSubRegIndex *SubRegIdx; bool IsDef; bool IsDead; public: TempRegRenderer(unsigned InsnID, unsigned TempRegID, bool IsDef = false, const CodeGenSubRegIndex *SubReg = nullptr, bool IsDead = false) : OperandRenderer(OR_Register), InsnID(InsnID), TempRegID(TempRegID), SubRegIdx(SubReg), IsDef(IsDef), IsDead(IsDead) {} static bool classof(const OperandRenderer *R) { return R->getKind() == OR_TempRegister; } void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Adds a specific immediate to the instruction being built. /// If a LLT is passed, a ConstantInt immediate is created instead. class ImmRenderer : public OperandRenderer { protected: unsigned InsnID; int64_t Imm; std::optional CImmLLT; public: ImmRenderer(unsigned InsnID, int64_t Imm) : OperandRenderer(OR_Imm), InsnID(InsnID), Imm(Imm) {} ImmRenderer(unsigned InsnID, int64_t Imm, const LLTCodeGenOrTempType &CImmLLT) : OperandRenderer(OR_Imm), InsnID(InsnID), Imm(Imm), CImmLLT(CImmLLT) { if (CImmLLT.isLLTCodeGen()) KnownTypes.insert(CImmLLT.getLLTCodeGen()); } static bool classof(const OperandRenderer *R) { return R->getKind() == OR_Imm; } static void emitAddImm(MatchTable &Table, RuleMatcher &RM, unsigned InsnID, int64_t Imm, StringRef ImmName = "Imm"); void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Adds an enum value for a subreg index to the instruction being built. class SubRegIndexRenderer : public OperandRenderer { protected: unsigned InsnID; const CodeGenSubRegIndex *SubRegIdx; public: SubRegIndexRenderer(unsigned InsnID, const CodeGenSubRegIndex *SRI) : OperandRenderer(OR_SubRegIndex), InsnID(InsnID), SubRegIdx(SRI) {} static bool classof(const OperandRenderer *R) { return R->getKind() == OR_SubRegIndex; } void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Adds operands by calling a renderer function supplied by the ComplexPattern /// matcher function. class RenderComplexPatternOperand : public OperandRenderer { private: unsigned InsnID; const Record &TheDef; /// The name of the operand. const StringRef SymbolicName; /// The renderer number. This must be unique within a rule since it's used to /// identify a temporary variable to hold the renderer function. unsigned RendererID; /// When provided, this is the suboperand of the ComplexPattern operand to /// render. Otherwise all the suboperands will be rendered. std::optional SubOperand; /// The subregister to extract. Render the whole register if not specified. const CodeGenSubRegIndex *SubReg; unsigned getNumOperands() const { return TheDef.getValueAsDag("Operands")->getNumArgs(); } public: RenderComplexPatternOperand(unsigned InsnID, const Record &TheDef, StringRef SymbolicName, unsigned RendererID, std::optional SubOperand = std::nullopt, const CodeGenSubRegIndex *SubReg = nullptr) : OperandRenderer(OR_ComplexPattern), InsnID(InsnID), TheDef(TheDef), SymbolicName(SymbolicName), RendererID(RendererID), SubOperand(SubOperand), SubReg(SubReg) {} static bool classof(const OperandRenderer *R) { return R->getKind() == OR_ComplexPattern; } void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Adds an intrinsic ID operand to the instruction being built. class IntrinsicIDRenderer : public OperandRenderer { protected: unsigned InsnID; const CodeGenIntrinsic *II; public: IntrinsicIDRenderer(unsigned InsnID, const CodeGenIntrinsic *II) : OperandRenderer(OR_Intrinsic), InsnID(InsnID), II(II) {} static bool classof(const OperandRenderer *R) { return R->getKind() == OR_Intrinsic; } void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; class CustomRenderer : public OperandRenderer { protected: unsigned InsnID; const Record &Renderer; /// The name of the operand. const std::string SymbolicName; public: CustomRenderer(unsigned InsnID, const Record &Renderer, StringRef SymbolicName) : OperandRenderer(OR_Custom), InsnID(InsnID), Renderer(Renderer), SymbolicName(SymbolicName) {} static bool classof(const OperandRenderer *R) { return R->getKind() == OR_Custom; } void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; class CustomOperandRenderer : public OperandRenderer { protected: unsigned InsnID; const Record &Renderer; /// The name of the operand. const std::string SymbolicName; public: CustomOperandRenderer(unsigned InsnID, const Record &Renderer, StringRef SymbolicName) : OperandRenderer(OR_CustomOperand), InsnID(InsnID), Renderer(Renderer), SymbolicName(SymbolicName) {} static bool classof(const OperandRenderer *R) { return R->getKind() == OR_CustomOperand; } void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// An action taken when all Matcher predicates succeeded for a parent rule. /// /// Typical actions include: /// * Changing the opcode of an instruction. /// * Adding an operand to an instruction. class MatchAction { public: enum ActionKind { AK_DebugComment, AK_BuildMI, AK_BuildConstantMI, AK_EraseInst, AK_ReplaceReg, AK_ConstraintOpsToDef, AK_ConstraintOpsToRC, AK_MakeTempReg, }; MatchAction(ActionKind K) : Kind(K) {} ActionKind getKind() const { return Kind; } virtual ~MatchAction() {} // Some actions may need to add extra predicates to ensure they can run. virtual void emitAdditionalPredicates(MatchTable &Table, RuleMatcher &Rule) const {} /// Emit the MatchTable opcodes to implement the action. virtual void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const = 0; /// If this opcode has an overload that can call GIR_Done directly, emit that /// instead of the usual opcode and return "true". Return "false" if GIR_Done /// still needs to be emitted. virtual bool emitActionOpcodesAndDone(MatchTable &Table, RuleMatcher &Rule) const { emitActionOpcodes(Table, Rule); return false; } private: ActionKind Kind; }; /// Generates a comment describing the matched rule being acted upon. class DebugCommentAction : public MatchAction { private: std::string S; public: DebugCommentAction(StringRef S) : MatchAction(AK_DebugComment), S(std::string(S)) {} static bool classof(const MatchAction *A) { return A->getKind() == AK_DebugComment; } void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override { Table << MatchTable::Comment(S) << MatchTable::LineBreak; } }; /// Generates code to build an instruction or mutate an existing instruction /// into the desired instruction when this is possible. class BuildMIAction : public MatchAction { private: unsigned InsnID; const CodeGenInstruction *I; InstructionMatcher *Matched; std::vector> OperandRenderers; SmallPtrSet DeadImplicitDefs; std::vector CopiedFlags; std::vector SetFlags; std::vector UnsetFlags; /// True if the instruction can be built solely by mutating the opcode. bool canMutate(RuleMatcher &Rule, const InstructionMatcher *Insn) const; public: BuildMIAction(unsigned InsnID, const CodeGenInstruction *I) : MatchAction(AK_BuildMI), InsnID(InsnID), I(I), Matched(nullptr) {} static bool classof(const MatchAction *A) { return A->getKind() == AK_BuildMI; } unsigned getInsnID() const { return InsnID; } const CodeGenInstruction *getCGI() const { return I; } void addSetMIFlags(StringRef Flag) { SetFlags.push_back(Flag); } void addUnsetMIFlags(StringRef Flag) { UnsetFlags.push_back(Flag); } void addCopiedMIFlags(const InstructionMatcher &IM) { CopiedFlags.push_back(&IM); } void chooseInsnToMutate(RuleMatcher &Rule); void setDeadImplicitDef(Record *R) { DeadImplicitDefs.insert(R); } template Kind &addRenderer(Args &&...args) { OperandRenderers.emplace_back( std::make_unique(InsnID, std::forward(args)...)); return *static_cast(OperandRenderers.back().get()); } void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to create a constant that defines a TempReg. /// The instruction created is usually a G_CONSTANT but it could also be a /// G_BUILD_VECTOR for vector types. class BuildConstantAction : public MatchAction { unsigned TempRegID; int64_t Val; public: BuildConstantAction(unsigned TempRegID, int64_t Val) : MatchAction(AK_BuildConstantMI), TempRegID(TempRegID), Val(Val) {} static bool classof(const MatchAction *A) { return A->getKind() == AK_BuildConstantMI; } void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; class EraseInstAction : public MatchAction { unsigned InsnID; public: EraseInstAction(unsigned InsnID) : MatchAction(AK_EraseInst), InsnID(InsnID) {} unsigned getInsnID() const { return InsnID; } static bool classof(const MatchAction *A) { return A->getKind() == AK_EraseInst; } void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; bool emitActionOpcodesAndDone(MatchTable &Table, RuleMatcher &Rule) const override; }; class ReplaceRegAction : public MatchAction { unsigned OldInsnID, OldOpIdx; unsigned NewInsnId = -1, NewOpIdx; unsigned TempRegID = -1; public: ReplaceRegAction(unsigned OldInsnID, unsigned OldOpIdx, unsigned NewInsnId, unsigned NewOpIdx) : MatchAction(AK_ReplaceReg), OldInsnID(OldInsnID), OldOpIdx(OldOpIdx), NewInsnId(NewInsnId), NewOpIdx(NewOpIdx) {} ReplaceRegAction(unsigned OldInsnID, unsigned OldOpIdx, unsigned TempRegID) : MatchAction(AK_ReplaceReg), OldInsnID(OldInsnID), OldOpIdx(OldOpIdx), TempRegID(TempRegID) {} static bool classof(const MatchAction *A) { return A->getKind() == AK_ReplaceReg; } void emitAdditionalPredicates(MatchTable &Table, RuleMatcher &Rule) const override; void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to constrain the operands of an output instruction to the /// register classes specified by the definition of that instruction. class ConstrainOperandsToDefinitionAction : public MatchAction { unsigned InsnID; public: ConstrainOperandsToDefinitionAction(unsigned InsnID) : MatchAction(AK_ConstraintOpsToDef), InsnID(InsnID) {} static bool classof(const MatchAction *A) { return A->getKind() == AK_ConstraintOpsToDef; } void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override { if (InsnID == 0) { Table << MatchTable::Opcode("GIR_RootConstrainSelectedInstOperands") << MatchTable::LineBreak; } else { Table << MatchTable::Opcode("GIR_ConstrainSelectedInstOperands") << MatchTable::Comment("InsnID") << MatchTable::ULEB128Value(InsnID) << MatchTable::LineBreak; } } }; /// Generates code to constrain the specified operand of an output instruction /// to the specified register class. class ConstrainOperandToRegClassAction : public MatchAction { unsigned InsnID; unsigned OpIdx; const CodeGenRegisterClass &RC; public: ConstrainOperandToRegClassAction(unsigned InsnID, unsigned OpIdx, const CodeGenRegisterClass &RC) : MatchAction(AK_ConstraintOpsToRC), InsnID(InsnID), OpIdx(OpIdx), RC(RC) {} static bool classof(const MatchAction *A) { return A->getKind() == AK_ConstraintOpsToRC; } void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; /// Generates code to create a temporary register which can be used to chain /// instructions together. class MakeTempRegisterAction : public MatchAction { private: LLTCodeGenOrTempType Ty; unsigned TempRegID; public: MakeTempRegisterAction(const LLTCodeGenOrTempType &Ty, unsigned TempRegID) : MatchAction(AK_MakeTempReg), Ty(Ty), TempRegID(TempRegID) { if (Ty.isLLTCodeGen()) KnownTypes.insert(Ty.getLLTCodeGen()); } static bool classof(const MatchAction *A) { return A->getKind() == AK_MakeTempReg; } void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override; }; } // namespace gi } // namespace llvm #endif