//===- GlobalISelEmitter.cpp - Generate an instruction selector -----------===// // // 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 tablegen backend emits code for use by the GlobalISel instruction /// selector. See include/llvm/Target/GlobalISel/Target.td. /// /// This file analyzes the patterns recognized by the SelectionDAGISel tablegen /// backend, filters out the ones that are unsupported, maps /// SelectionDAG-specific constructs to their GlobalISel counterpart /// (when applicable: MVT to LLT; SDNode to generic Instruction). /// /// Not all patterns are supported: pass the tablegen invocation /// "-warn-on-skipped-patterns" to emit a warning when a pattern is skipped, /// as well as why. /// /// The generated file defines a single method: /// bool InstructionSelector::selectImpl(MachineInstr &I) const; /// intended to be used in InstructionSelector::select as the first-step /// selector for the patterns that don't require complex C++. /// /// FIXME: We'll probably want to eventually define a base /// "TargetGenInstructionSelector" class. /// //===----------------------------------------------------------------------===// #include "Basic/CodeGenIntrinsics.h" #include "Common/CodeGenDAGPatterns.h" #include "Common/CodeGenInstruction.h" #include "Common/CodeGenRegisters.h" #include "Common/CodeGenTarget.h" #include "Common/GlobalISel/GlobalISelMatchTable.h" #include "Common/GlobalISel/GlobalISelMatchTableExecutorEmitter.h" #include "Common/InfoByHwMode.h" #include "Common/SubtargetFeatureInfo.h" #include "llvm/ADT/Statistic.h" #include "llvm/CodeGenTypes/LowLevelType.h" #include "llvm/CodeGenTypes/MachineValueType.h" #include "llvm/Support/CodeGenCoverage.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" #include using namespace llvm; using namespace llvm::gi; using action_iterator = RuleMatcher::action_iterator; #define DEBUG_TYPE "gisel-emitter" STATISTIC(NumPatternTotal, "Total number of patterns"); STATISTIC(NumPatternImported, "Number of patterns imported from SelectionDAG"); STATISTIC(NumPatternImportsSkipped, "Number of SelectionDAG imports skipped"); STATISTIC(NumPatternsTested, "Number of patterns executed according to coverage information"); cl::OptionCategory GlobalISelEmitterCat("Options for -gen-global-isel"); static cl::opt WarnOnSkippedPatterns( "warn-on-skipped-patterns", cl::desc("Explain why a pattern was skipped for inclusion " "in the GlobalISel selector"), cl::init(false), cl::cat(GlobalISelEmitterCat)); static cl::opt GenerateCoverage( "instrument-gisel-coverage", cl::desc("Generate coverage instrumentation for GlobalISel"), cl::init(false), cl::cat(GlobalISelEmitterCat)); static cl::opt UseCoverageFile( "gisel-coverage-file", cl::init(""), cl::desc("Specify file to retrieve coverage information from"), cl::cat(GlobalISelEmitterCat)); static cl::opt OptimizeMatchTable( "optimize-match-table", cl::desc("Generate an optimized version of the match table"), cl::init(true), cl::cat(GlobalISelEmitterCat)); namespace { static std::string explainPredicates(const TreePatternNode &N) { std::string Explanation; StringRef Separator = ""; for (const TreePredicateCall &Call : N.getPredicateCalls()) { const TreePredicateFn &P = Call.Fn; Explanation += (Separator + P.getOrigPatFragRecord()->getRecord()->getName()).str(); Separator = ", "; if (P.isAlwaysTrue()) Explanation += " always-true"; if (P.isImmediatePattern()) Explanation += " immediate"; if (P.isUnindexed()) Explanation += " unindexed"; if (P.isNonExtLoad()) Explanation += " non-extload"; if (P.isAnyExtLoad()) Explanation += " extload"; if (P.isSignExtLoad()) Explanation += " sextload"; if (P.isZeroExtLoad()) Explanation += " zextload"; if (P.isNonTruncStore()) Explanation += " non-truncstore"; if (P.isTruncStore()) Explanation += " truncstore"; if (Record *VT = P.getMemoryVT()) Explanation += (" MemVT=" + VT->getName()).str(); if (Record *VT = P.getScalarMemoryVT()) Explanation += (" ScalarVT(MemVT)=" + VT->getName()).str(); if (ListInit *AddrSpaces = P.getAddressSpaces()) { raw_string_ostream OS(Explanation); OS << " AddressSpaces=["; StringRef AddrSpaceSeparator; for (Init *Val : AddrSpaces->getValues()) { IntInit *IntVal = dyn_cast(Val); if (!IntVal) continue; OS << AddrSpaceSeparator << IntVal->getValue(); AddrSpaceSeparator = ", "; } OS << ']'; } int64_t MinAlign = P.getMinAlignment(); if (MinAlign > 0) Explanation += " MinAlign=" + utostr(MinAlign); if (P.isAtomicOrderingMonotonic()) Explanation += " monotonic"; if (P.isAtomicOrderingAcquire()) Explanation += " acquire"; if (P.isAtomicOrderingRelease()) Explanation += " release"; if (P.isAtomicOrderingAcquireRelease()) Explanation += " acq_rel"; if (P.isAtomicOrderingSequentiallyConsistent()) Explanation += " seq_cst"; if (P.isAtomicOrderingAcquireOrStronger()) Explanation += " >=acquire"; if (P.isAtomicOrderingWeakerThanAcquire()) Explanation += " isSubClassOf("SDNode")) return (" (" + Operator->getValueAsString("Opcode") + ")").str(); if (Operator->isSubClassOf("Intrinsic")) return (" (Operator is an Intrinsic, " + Operator->getName() + ")").str(); if (Operator->isSubClassOf("ComplexPattern")) return (" (Operator is an unmapped ComplexPattern, " + Operator->getName() + ")") .str(); if (Operator->isSubClassOf("SDNodeXForm")) return (" (Operator is an unmapped SDNodeXForm, " + Operator->getName() + ")") .str(); return (" (Operator " + Operator->getName() + " not understood)").str(); } /// Helper function to let the emitter report skip reason error messages. static Error failedImport(const Twine &Reason) { return make_error(Reason, inconvertibleErrorCode()); } static Error isTrivialOperatorNode(const TreePatternNode &N) { std::string Explanation; std::string Separator; bool HasUnsupportedPredicate = false; for (const TreePredicateCall &Call : N.getPredicateCalls()) { const TreePredicateFn &Predicate = Call.Fn; if (Predicate.isAlwaysTrue()) continue; if (Predicate.isImmediatePattern()) continue; if (Predicate.hasNoUse() || Predicate.hasOneUse()) continue; if (Predicate.isNonExtLoad() || Predicate.isAnyExtLoad() || Predicate.isSignExtLoad() || Predicate.isZeroExtLoad()) continue; if (Predicate.isNonTruncStore() || Predicate.isTruncStore()) continue; if (Predicate.isLoad() && Predicate.getMemoryVT()) continue; if (Predicate.isLoad() || Predicate.isStore()) { if (Predicate.isUnindexed()) continue; } if (Predicate.isLoad() || Predicate.isStore() || Predicate.isAtomic()) { const ListInit *AddrSpaces = Predicate.getAddressSpaces(); if (AddrSpaces && !AddrSpaces->empty()) continue; if (Predicate.getMinAlignment() > 0) continue; } if (Predicate.isAtomic() && Predicate.getMemoryVT()) continue; if (Predicate.isAtomic() && (Predicate.isAtomicOrderingMonotonic() || Predicate.isAtomicOrderingAcquire() || Predicate.isAtomicOrderingRelease() || Predicate.isAtomicOrderingAcquireRelease() || Predicate.isAtomicOrderingSequentiallyConsistent() || Predicate.isAtomicOrderingAcquireOrStronger() || Predicate.isAtomicOrderingWeakerThanAcquire() || Predicate.isAtomicOrderingReleaseOrStronger() || Predicate.isAtomicOrderingWeakerThanRelease())) continue; if (Predicate.hasGISelPredicateCode()) continue; HasUnsupportedPredicate = true; Explanation = Separator + "Has a predicate (" + explainPredicates(N) + ")"; Separator = ", "; Explanation += (Separator + "first-failing:" + Predicate.getOrigPatFragRecord()->getRecord()->getName()) .str(); break; } if (!HasUnsupportedPredicate) return Error::success(); return failedImport(Explanation); } static Record *getInitValueAsRegClass(Init *V) { if (DefInit *VDefInit = dyn_cast(V)) { if (VDefInit->getDef()->isSubClassOf("RegisterOperand")) return VDefInit->getDef()->getValueAsDef("RegClass"); if (VDefInit->getDef()->isSubClassOf("RegisterClass")) return VDefInit->getDef(); } return nullptr; } static std::string getScopedName(unsigned Scope, const std::string &Name) { return ("pred:" + Twine(Scope) + ":" + Name).str(); } static std::string getMangledRootDefName(StringRef DefOperandName) { return ("DstI[" + DefOperandName + "]").str(); } //===- GlobalISelEmitter class --------------------------------------------===// static Expected getInstResultType(const TreePatternNode &Dst, const CodeGenTarget &Target) { // While we allow more than one output (both implicit and explicit defs) // below, we only expect one explicit def here. assert(Dst.getOperator()->isSubClassOf("Instruction")); CodeGenInstruction &InstInfo = Target.getInstruction(Dst.getOperator()); if (!InstInfo.Operands.NumDefs) return failedImport("Dst pattern child needs a def"); ArrayRef ChildTypes = Dst.getExtTypes(); if (ChildTypes.size() < 1) return failedImport("Dst pattern child has no result"); // If there are multiple results, just take the first one (this is how // SelectionDAG does it). std::optional MaybeOpTy; if (ChildTypes.front().isMachineValueType()) { MaybeOpTy = MVTToLLT(ChildTypes.front().getMachineValueType().SimpleTy); } if (!MaybeOpTy) return failedImport("Dst operand has an unsupported type"); return *MaybeOpTy; } class GlobalISelEmitter final : public GlobalISelMatchTableExecutorEmitter { public: explicit GlobalISelEmitter(RecordKeeper &RK); 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 postProcessRule(RuleMatcher &M); const CodeGenTarget &getTarget() const override { return Target; } StringRef getClassName() const override { return ClassName; } void run(raw_ostream &OS); private: std::string ClassName; const RecordKeeper &RK; const CodeGenDAGPatterns CGP; const CodeGenTarget &Target; CodeGenRegBank &CGRegs; std::vector AllPatFrags; /// Keep track of the equivalence between SDNodes and Instruction by mapping /// SDNodes to the GINodeEquiv mapping. We need to map to the GINodeEquiv to /// check for attributes on the relation such as CheckMMOIsNonAtomic. /// This is defined using 'GINodeEquiv' in the target description. DenseMap NodeEquivs; /// Keep track of the equivalence between ComplexPattern's and /// GIComplexOperandMatcher. Map entries are specified by subclassing /// GIComplexPatternEquiv. DenseMap ComplexPatternEquivs; /// Keep track of the equivalence between SDNodeXForm's and /// GICustomOperandRenderer. Map entries are specified by subclassing /// GISDNodeXFormEquiv. DenseMap SDNodeXFormEquivs; /// Keep track of Scores of PatternsToMatch similar to how the DAG does. /// This adds compatibility for RuleMatchers to use this for ordering rules. DenseMap RuleMatcherScores; // Rule coverage information. std::optional RuleCoverage; /// Variables used to help with collecting of named operands for predicates /// with 'let PredicateCodeUsesOperands = 1'. WaitingForNamedOperands is set /// to the number of named operands that predicate expects. Store locations in /// StoreIdxForName correspond to the order in which operand names appear in /// predicate's argument list. /// When we visit named operand and WaitingForNamedOperands is not zero, add /// matcher that will record operand and decrease counter. unsigned WaitingForNamedOperands = 0; StringMap StoreIdxForName; void gatherOpcodeValues(); void gatherTypeIDValues(); void gatherNodeEquivs(); Record *findNodeEquiv(Record *N) const; const CodeGenInstruction *getEquivNode(Record &Equiv, const TreePatternNode &N) const; Error importRulePredicates(RuleMatcher &M, ArrayRef Predicates); Expected createAndImportSelDAGMatcher(RuleMatcher &Rule, InstructionMatcher &InsnMatcher, const TreePatternNode &Src, unsigned &TempOpIdx); Error importComplexPatternOperandMatcher(OperandMatcher &OM, Record *R, unsigned &TempOpIdx) const; Error importChildMatcher(RuleMatcher &Rule, InstructionMatcher &InsnMatcher, const TreePatternNode &SrcChild, bool OperandIsAPointer, bool OperandIsImmArg, unsigned OpIdx, unsigned &TempOpIdx); Expected createAndImportInstructionRenderer( RuleMatcher &M, InstructionMatcher &InsnMatcher, const TreePatternNode &Src, const TreePatternNode &Dst); Expected createAndImportSubInstructionRenderer( action_iterator InsertPt, RuleMatcher &M, const TreePatternNode &Dst, const TreePatternNode &Src, unsigned TempReg); Expected createInstructionRenderer(action_iterator InsertPt, RuleMatcher &M, const TreePatternNode &Dst); Expected importExplicitDefRenderers(action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder, const TreePatternNode &Src, const TreePatternNode &Dst, unsigned Start = 0); Expected importExplicitUseRenderers( action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder, const llvm::TreePatternNode &Dst, const TreePatternNode &Src); Expected importExplicitUseRenderer( action_iterator InsertPt, RuleMatcher &Rule, BuildMIAction &DstMIBuilder, const TreePatternNode &DstChild, const TreePatternNode &Src); Error importDefaultOperandRenderers(action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder, const DAGDefaultOperand &DefaultOp) const; Error importImplicitDefRenderers(BuildMIAction &DstMIBuilder, const std::vector &ImplicitDefs) const; /// Analyze pattern \p P, returning a matcher for it if possible. /// Otherwise, return an Error explaining why we don't support it. Expected runOnPattern(const PatternToMatch &P); void declareSubtargetFeature(Record *Predicate); unsigned declareHwModeCheck(StringRef HwModeFeatures); MatchTable buildMatchTable(MutableArrayRef Rules, bool Optimize, bool WithCoverage); /// Infer a CodeGenRegisterClass for the type of \p SuperRegNode. The returned /// CodeGenRegisterClass will support the CodeGenRegisterClass of /// \p SubRegNode, and the subregister index defined by \p SubRegIdxNode. /// If no register class is found, return std::nullopt. std::optional inferSuperRegisterClassForNode(const TypeSetByHwMode &Ty, const TreePatternNode &SuperRegNode, const TreePatternNode &SubRegIdxNode); std::optional inferSubRegIndexForNode(const TreePatternNode &SubRegIdxNode); /// Infer a CodeGenRegisterClass which suppoorts \p Ty and \p SubRegIdxNode. /// Return std::nullopt if no such class exists. std::optional inferSuperRegisterClass(const TypeSetByHwMode &Ty, const TreePatternNode &SubRegIdxNode); /// Return the CodeGenRegisterClass associated with \p Leaf if it has one. std::optional getRegClassFromLeaf(const TreePatternNode &Leaf); /// Return a CodeGenRegisterClass for \p N if one can be found. Return /// std::nullopt otherwise. std::optional inferRegClassFromPattern(const TreePatternNode &N); /// Return the size of the MemoryVT in this predicate, if possible. std::optional getMemSizeBitsFromPredicate(const TreePredicateFn &Predicate); // Add builtin predicates. Expected addBuiltinPredicates(const Record *SrcGIEquivOrNull, const TreePredicateFn &Predicate, InstructionMatcher &InsnMatcher, bool &HasAddedMatcher); }; StringRef getPatFragPredicateEnumName(Record *R) { return R->getName(); } void GlobalISelEmitter::gatherOpcodeValues() { InstructionOpcodeMatcher::initOpcodeValuesMap(Target); } void GlobalISelEmitter::gatherTypeIDValues() { LLTOperandMatcher::initTypeIDValuesMap(); } void GlobalISelEmitter::gatherNodeEquivs() { assert(NodeEquivs.empty()); for (Record *Equiv : RK.getAllDerivedDefinitions("GINodeEquiv")) NodeEquivs[Equiv->getValueAsDef("Node")] = Equiv; assert(ComplexPatternEquivs.empty()); for (Record *Equiv : RK.getAllDerivedDefinitions("GIComplexPatternEquiv")) { Record *SelDAGEquiv = Equiv->getValueAsDef("SelDAGEquivalent"); if (!SelDAGEquiv) continue; ComplexPatternEquivs[SelDAGEquiv] = Equiv; } assert(SDNodeXFormEquivs.empty()); for (Record *Equiv : RK.getAllDerivedDefinitions("GISDNodeXFormEquiv")) { Record *SelDAGEquiv = Equiv->getValueAsDef("SelDAGEquivalent"); if (!SelDAGEquiv) continue; SDNodeXFormEquivs[SelDAGEquiv] = Equiv; } } Record *GlobalISelEmitter::findNodeEquiv(Record *N) const { return NodeEquivs.lookup(N); } const CodeGenInstruction * GlobalISelEmitter::getEquivNode(Record &Equiv, const TreePatternNode &N) const { if (N.getNumChildren() >= 1) { // setcc operation maps to two different G_* instructions based on the type. if (!Equiv.isValueUnset("IfFloatingPoint") && MVT(N.getChild(0).getSimpleType(0)).isFloatingPoint()) return &Target.getInstruction(Equiv.getValueAsDef("IfFloatingPoint")); } if (!Equiv.isValueUnset("IfConvergent") && N.getIntrinsicInfo(CGP)->isConvergent) return &Target.getInstruction(Equiv.getValueAsDef("IfConvergent")); for (const TreePredicateCall &Call : N.getPredicateCalls()) { const TreePredicateFn &Predicate = Call.Fn; if (!Equiv.isValueUnset("IfSignExtend") && (Predicate.isLoad() || Predicate.isAtomic()) && Predicate.isSignExtLoad()) return &Target.getInstruction(Equiv.getValueAsDef("IfSignExtend")); if (!Equiv.isValueUnset("IfZeroExtend") && (Predicate.isLoad() || Predicate.isAtomic()) && Predicate.isZeroExtLoad()) return &Target.getInstruction(Equiv.getValueAsDef("IfZeroExtend")); } return &Target.getInstruction(Equiv.getValueAsDef("I")); } GlobalISelEmitter::GlobalISelEmitter(RecordKeeper &RK) : GlobalISelMatchTableExecutorEmitter(), RK(RK), CGP(RK), Target(CGP.getTargetInfo()), CGRegs(Target.getRegBank()) { ClassName = Target.getName().str() + "InstructionSelector"; } //===- Emitter ------------------------------------------------------------===// Error GlobalISelEmitter::importRulePredicates(RuleMatcher &M, ArrayRef Predicates) { for (Record *Pred : Predicates) { if (Pred->getValueAsString("CondString").empty()) continue; declareSubtargetFeature(Pred); M.addRequiredFeature(Pred); } return Error::success(); } std::optional GlobalISelEmitter::getMemSizeBitsFromPredicate( const TreePredicateFn &Predicate) { std::optional MemTyOrNone = MVTToLLT(getValueType(Predicate.getMemoryVT())); if (!MemTyOrNone) return std::nullopt; // Align so unusual types like i1 don't get rounded down. return llvm::alignTo( static_cast(MemTyOrNone->get().getSizeInBits()), 8); } Expected GlobalISelEmitter::addBuiltinPredicates( const Record *SrcGIEquivOrNull, const TreePredicateFn &Predicate, InstructionMatcher &InsnMatcher, bool &HasAddedMatcher) { if (Predicate.isLoad() || Predicate.isStore() || Predicate.isAtomic()) { if (const ListInit *AddrSpaces = Predicate.getAddressSpaces()) { SmallVector ParsedAddrSpaces; for (Init *Val : AddrSpaces->getValues()) { IntInit *IntVal = dyn_cast(Val); if (!IntVal) return failedImport("Address space is not an integer"); ParsedAddrSpaces.push_back(IntVal->getValue()); } if (!ParsedAddrSpaces.empty()) { InsnMatcher.addPredicate( 0, ParsedAddrSpaces); return InsnMatcher; } } int64_t MinAlign = Predicate.getMinAlignment(); if (MinAlign > 0) { InsnMatcher.addPredicate(0, MinAlign); return InsnMatcher; } } // G_LOAD is used for both non-extending and any-extending loads. if (Predicate.isLoad() && Predicate.isNonExtLoad()) { InsnMatcher.addPredicate( 0, MemoryVsLLTSizePredicateMatcher::EqualTo, 0); return InsnMatcher; } if (Predicate.isLoad() && Predicate.isAnyExtLoad()) { InsnMatcher.addPredicate( 0, MemoryVsLLTSizePredicateMatcher::LessThan, 0); return InsnMatcher; } if (Predicate.isStore()) { if (Predicate.isTruncStore()) { if (Predicate.getMemoryVT() != nullptr) { // FIXME: If MemoryVT is set, we end up with 2 checks for the MMO size. auto MemSizeInBits = getMemSizeBitsFromPredicate(Predicate); if (!MemSizeInBits) return failedImport("MemVT could not be converted to LLT"); InsnMatcher.addPredicate(0, *MemSizeInBits / 8); } else { InsnMatcher.addPredicate( 0, MemoryVsLLTSizePredicateMatcher::LessThan, 0); } return InsnMatcher; } if (Predicate.isNonTruncStore()) { // We need to check the sizes match here otherwise we could incorrectly // match truncating stores with non-truncating ones. InsnMatcher.addPredicate( 0, MemoryVsLLTSizePredicateMatcher::EqualTo, 0); } } assert(SrcGIEquivOrNull != nullptr && "Invalid SrcGIEquivOrNull value"); // No check required. We already did it by swapping the opcode. if (!SrcGIEquivOrNull->isValueUnset("IfSignExtend") && Predicate.isSignExtLoad()) return InsnMatcher; // No check required. We already did it by swapping the opcode. if (!SrcGIEquivOrNull->isValueUnset("IfZeroExtend") && Predicate.isZeroExtLoad()) return InsnMatcher; // No check required. G_STORE by itself is a non-extending store. if (Predicate.isNonTruncStore()) return InsnMatcher; if (Predicate.isLoad() || Predicate.isStore() || Predicate.isAtomic()) { if (Predicate.getMemoryVT() != nullptr) { auto MemSizeInBits = getMemSizeBitsFromPredicate(Predicate); if (!MemSizeInBits) return failedImport("MemVT could not be converted to LLT"); InsnMatcher.addPredicate(0, *MemSizeInBits / 8); return InsnMatcher; } } if (Predicate.isLoad() || Predicate.isStore()) { // No check required. A G_LOAD/G_STORE is an unindexed load. if (Predicate.isUnindexed()) return InsnMatcher; } if (Predicate.isAtomic()) { if (Predicate.isAtomicOrderingMonotonic()) { InsnMatcher.addPredicate("Monotonic"); return InsnMatcher; } if (Predicate.isAtomicOrderingAcquire()) { InsnMatcher.addPredicate("Acquire"); return InsnMatcher; } if (Predicate.isAtomicOrderingRelease()) { InsnMatcher.addPredicate("Release"); return InsnMatcher; } if (Predicate.isAtomicOrderingAcquireRelease()) { InsnMatcher.addPredicate( "AcquireRelease"); return InsnMatcher; } if (Predicate.isAtomicOrderingSequentiallyConsistent()) { InsnMatcher.addPredicate( "SequentiallyConsistent"); return InsnMatcher; } } if (Predicate.isAtomicOrderingAcquireOrStronger()) { InsnMatcher.addPredicate( "Acquire", AtomicOrderingMMOPredicateMatcher::AO_OrStronger); return InsnMatcher; } if (Predicate.isAtomicOrderingWeakerThanAcquire()) { InsnMatcher.addPredicate( "Acquire", AtomicOrderingMMOPredicateMatcher::AO_WeakerThan); return InsnMatcher; } if (Predicate.isAtomicOrderingReleaseOrStronger()) { InsnMatcher.addPredicate( "Release", AtomicOrderingMMOPredicateMatcher::AO_OrStronger); return InsnMatcher; } if (Predicate.isAtomicOrderingWeakerThanRelease()) { InsnMatcher.addPredicate( "Release", AtomicOrderingMMOPredicateMatcher::AO_WeakerThan); return InsnMatcher; } HasAddedMatcher = false; return InsnMatcher; } Expected GlobalISelEmitter::createAndImportSelDAGMatcher( RuleMatcher &Rule, InstructionMatcher &InsnMatcher, const TreePatternNode &Src, unsigned &TempOpIdx) { const auto SavedFlags = Rule.setGISelFlags(Src.getGISelFlagsRecord()); Record *SrcGIEquivOrNull = nullptr; const CodeGenInstruction *SrcGIOrNull = nullptr; // Start with the defined operands (i.e., the results of the root operator). if (Src.isLeaf()) { Init *SrcInit = Src.getLeafValue(); if (isa(SrcInit)) { InsnMatcher.addPredicate( &Target.getInstruction(RK.getDef("G_CONSTANT"))); } else return failedImport( "Unable to deduce gMIR opcode to handle Src (which is a leaf)"); } else { SrcGIEquivOrNull = findNodeEquiv(Src.getOperator()); if (!SrcGIEquivOrNull) return failedImport("Pattern operator lacks an equivalent Instruction" + explainOperator(Src.getOperator())); SrcGIOrNull = getEquivNode(*SrcGIEquivOrNull, Src); // The operators look good: match the opcode InsnMatcher.addPredicate(SrcGIOrNull); } unsigned OpIdx = 0; for (const TypeSetByHwMode &VTy : Src.getExtTypes()) { // Results don't have a name unless they are the root node. The caller will // set the name if appropriate. const bool OperandIsAPointer = SrcGIOrNull && SrcGIOrNull->isOutOperandAPointer(OpIdx); OperandMatcher &OM = InsnMatcher.addOperand(OpIdx++, "", TempOpIdx); if (auto Error = OM.addTypeCheckPredicate(VTy, OperandIsAPointer)) return failedImport(toString(std::move(Error)) + " for result of Src pattern operator"); } for (const TreePredicateCall &Call : Src.getPredicateCalls()) { const TreePredicateFn &Predicate = Call.Fn; bool HasAddedBuiltinMatcher = true; if (Predicate.isAlwaysTrue()) continue; if (Predicate.isImmediatePattern()) { InsnMatcher.addPredicate(Predicate); continue; } auto InsnMatcherOrError = addBuiltinPredicates( SrcGIEquivOrNull, Predicate, InsnMatcher, HasAddedBuiltinMatcher); if (auto Error = InsnMatcherOrError.takeError()) return std::move(Error); // FIXME: This should be part of addBuiltinPredicates(). If we add this at // the start of addBuiltinPredicates() without returning, then there might // be cases where we hit the last return before which the // HasAddedBuiltinMatcher will be set to false. The predicate could be // missed if we add it in the middle or at the end due to return statements // after the addPredicate<>() calls. if (Predicate.hasNoUse()) { InsnMatcher.addPredicate(); HasAddedBuiltinMatcher = true; } if (Predicate.hasOneUse()) { InsnMatcher.addPredicate(); HasAddedBuiltinMatcher = true; } if (Predicate.hasGISelPredicateCode()) { if (Predicate.usesOperands()) { assert(WaitingForNamedOperands == 0 && "previous predicate didn't find all operands or " "nested predicate that uses operands"); TreePattern *TP = Predicate.getOrigPatFragRecord(); WaitingForNamedOperands = TP->getNumArgs(); for (unsigned I = 0; I < WaitingForNamedOperands; ++I) StoreIdxForName[getScopedName(Call.Scope, TP->getArgName(I))] = I; } InsnMatcher.addPredicate(Predicate); continue; } if (!HasAddedBuiltinMatcher) { return failedImport("Src pattern child has predicate (" + explainPredicates(Src) + ")"); } } if (SrcGIEquivOrNull && SrcGIEquivOrNull->getValueAsBit("CheckMMOIsNonAtomic")) InsnMatcher.addPredicate("NotAtomic"); else if (SrcGIEquivOrNull && SrcGIEquivOrNull->getValueAsBit("CheckMMOIsAtomic")) { InsnMatcher.addPredicate( "Unordered", AtomicOrderingMMOPredicateMatcher::AO_OrStronger); } if (Src.isLeaf()) { Init *SrcInit = Src.getLeafValue(); if (IntInit *SrcIntInit = dyn_cast(SrcInit)) { OperandMatcher &OM = InsnMatcher.addOperand(OpIdx++, Src.getName(), TempOpIdx); OM.addPredicate(SrcIntInit->getValue()); } else return failedImport( "Unable to deduce gMIR opcode to handle Src (which is a leaf)"); } else { assert(SrcGIOrNull && "Expected to have already found an equivalent Instruction"); if (SrcGIOrNull->TheDef->getName() == "G_CONSTANT" || SrcGIOrNull->TheDef->getName() == "G_FCONSTANT") { // imm/fpimm still have operands but we don't need to do anything with it // here since we don't support ImmLeaf predicates yet. However, we still // need to note the hidden operand to get GIM_CheckNumOperands correct. InsnMatcher.addOperand(OpIdx++, "", TempOpIdx); return InsnMatcher; } if (SrcGIOrNull->TheDef->getName() == "G_FRAME_INDEX") { InsnMatcher.addOperand(OpIdx++, Src.getName(), TempOpIdx); return InsnMatcher; } // Special case because the operand order is changed from setcc. The // predicate operand needs to be swapped from the last operand to the first // source. unsigned NumChildren = Src.getNumChildren(); bool IsFCmp = SrcGIOrNull->TheDef->getName() == "G_FCMP"; if (IsFCmp || SrcGIOrNull->TheDef->getName() == "G_ICMP") { const TreePatternNode &SrcChild = Src.getChild(NumChildren - 1); if (SrcChild.isLeaf()) { DefInit *DI = dyn_cast(SrcChild.getLeafValue()); Record *CCDef = DI ? DI->getDef() : nullptr; if (!CCDef || !CCDef->isSubClassOf("CondCode")) return failedImport("Unable to handle CondCode"); OperandMatcher &OM = InsnMatcher.addOperand(OpIdx++, SrcChild.getName(), TempOpIdx); StringRef PredType = IsFCmp ? CCDef->getValueAsString("FCmpPredicate") : CCDef->getValueAsString("ICmpPredicate"); if (!PredType.empty()) { OM.addPredicate(std::string(PredType)); // Process the other 2 operands normally. --NumChildren; } } } // Match the used operands (i.e. the children of the operator). bool IsIntrinsic = SrcGIOrNull->TheDef->getName() == "G_INTRINSIC" || SrcGIOrNull->TheDef->getName() == "G_INTRINSIC_W_SIDE_EFFECTS" || SrcGIOrNull->TheDef->getName() == "G_INTRINSIC_CONVERGENT" || SrcGIOrNull->TheDef->getName() == "G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS"; const CodeGenIntrinsic *II = Src.getIntrinsicInfo(CGP); if (IsIntrinsic && !II) return failedImport("Expected IntInit containing intrinsic ID)"); for (unsigned I = 0; I != NumChildren; ++I) { const TreePatternNode &SrcChild = Src.getChild(I); // We need to determine the meaning of a literal integer based on the // context. If this is a field required to be an immediate (such as an // immarg intrinsic argument), the required predicates are different than // a constant which may be materialized in a register. If we have an // argument that is required to be an immediate, we should not emit an LLT // type check, and should not be looking for a G_CONSTANT defined // register. bool OperandIsImmArg = SrcGIOrNull->isInOperandImmArg(I); // SelectionDAG allows pointers to be represented with iN since it doesn't // distinguish between pointers and integers but they are different types // in GlobalISel. Coerce integers to pointers to address space 0 if the // context indicates a pointer. // bool OperandIsAPointer = SrcGIOrNull->isInOperandAPointer(I); if (IsIntrinsic) { // For G_INTRINSIC/G_INTRINSIC_W_SIDE_EFFECTS, the operand immediately // following the defs is an intrinsic ID. if (I == 0) { OperandMatcher &OM = InsnMatcher.addOperand(OpIdx++, SrcChild.getName(), TempOpIdx); OM.addPredicate(II); continue; } // We have to check intrinsics for llvm_anyptr_ty and immarg parameters. // // Note that we have to look at the i-1th parameter, because we don't // have the intrinsic ID in the intrinsic's parameter list. OperandIsAPointer |= II->isParamAPointer(I - 1); OperandIsImmArg |= II->isParamImmArg(I - 1); } if (auto Error = importChildMatcher(Rule, InsnMatcher, SrcChild, OperandIsAPointer, OperandIsImmArg, OpIdx++, TempOpIdx)) return std::move(Error); } } return InsnMatcher; } Error GlobalISelEmitter::importComplexPatternOperandMatcher( OperandMatcher &OM, Record *R, unsigned &TempOpIdx) const { const auto &ComplexPattern = ComplexPatternEquivs.find(R); if (ComplexPattern == ComplexPatternEquivs.end()) return failedImport("SelectionDAG ComplexPattern (" + R->getName() + ") not mapped to GlobalISel"); OM.addPredicate(OM, *ComplexPattern->second); TempOpIdx++; return Error::success(); } // Get the name to use for a pattern operand. For an anonymous physical register // input, this should use the register name. static StringRef getSrcChildName(const TreePatternNode &SrcChild, Record *&PhysReg) { StringRef SrcChildName = SrcChild.getName(); if (SrcChildName.empty() && SrcChild.isLeaf()) { if (auto *ChildDefInit = dyn_cast(SrcChild.getLeafValue())) { auto *ChildRec = ChildDefInit->getDef(); if (ChildRec->isSubClassOf("Register")) { SrcChildName = ChildRec->getName(); PhysReg = ChildRec; } } } return SrcChildName; } Error GlobalISelEmitter::importChildMatcher( RuleMatcher &Rule, InstructionMatcher &InsnMatcher, const TreePatternNode &SrcChild, bool OperandIsAPointer, bool OperandIsImmArg, unsigned OpIdx, unsigned &TempOpIdx) { Record *PhysReg = nullptr; std::string SrcChildName = std::string(getSrcChildName(SrcChild, PhysReg)); if (!SrcChild.isLeaf() && SrcChild.getOperator()->isSubClassOf("ComplexPattern")) { // The "name" of a non-leaf complex pattern (MY_PAT $op1, $op2) is // "MY_PAT:op1:op2" and the ones with same "name" represent same operand. std::string PatternName = std::string(SrcChild.getOperator()->getName()); for (unsigned I = 0; I < SrcChild.getNumChildren(); ++I) { PatternName += ":"; PatternName += SrcChild.getChild(I).getName(); } SrcChildName = PatternName; } OperandMatcher &OM = PhysReg ? InsnMatcher.addPhysRegInput(PhysReg, OpIdx, TempOpIdx) : InsnMatcher.addOperand(OpIdx, SrcChildName, TempOpIdx); if (OM.isSameAsAnotherOperand()) return Error::success(); ArrayRef ChildTypes = SrcChild.getExtTypes(); if (ChildTypes.size() != 1) return failedImport("Src pattern child has multiple results"); // Check MBB's before the type check since they are not a known type. if (!SrcChild.isLeaf()) { if (SrcChild.getOperator()->isSubClassOf("SDNode")) { auto &ChildSDNI = CGP.getSDNodeInfo(SrcChild.getOperator()); if (ChildSDNI.getSDClassName() == "BasicBlockSDNode") { OM.addPredicate(); return Error::success(); } if (SrcChild.getOperator()->getName() == "timm") { OM.addPredicate(); // Add predicates, if any for (const TreePredicateCall &Call : SrcChild.getPredicateCalls()) { const TreePredicateFn &Predicate = Call.Fn; // Only handle immediate patterns for now if (Predicate.isImmediatePattern()) { OM.addPredicate(Predicate); } } return Error::success(); } } } // Immediate arguments have no meaningful type to check as they don't have // registers. if (!OperandIsImmArg) { if (auto Error = OM.addTypeCheckPredicate(ChildTypes.front(), OperandIsAPointer)) return failedImport(toString(std::move(Error)) + " for Src operand (" + to_string(SrcChild) + ")"); } // Try look up SrcChild for a (named) predicate operand if there is any. if (WaitingForNamedOperands) { auto &ScopedNames = SrcChild.getNamesAsPredicateArg(); if (!ScopedNames.empty()) { auto PA = ScopedNames.begin(); std::string Name = getScopedName(PA->getScope(), PA->getIdentifier()); OM.addPredicate(StoreIdxForName[Name], Name); --WaitingForNamedOperands; } } // Check for nested instructions. if (!SrcChild.isLeaf()) { if (SrcChild.getOperator()->isSubClassOf("ComplexPattern")) { // When a ComplexPattern is used as an operator, it should do the same // thing as when used as a leaf. However, the children of the operator // name the sub-operands that make up the complex operand and we must // prepare to reference them in the renderer too. unsigned RendererID = TempOpIdx; if (auto Error = importComplexPatternOperandMatcher( OM, SrcChild.getOperator(), TempOpIdx)) return Error; for (unsigned I = 0, E = SrcChild.getNumChildren(); I != E; ++I) { auto &SubOperand = SrcChild.getChild(I); if (!SubOperand.getName().empty()) { if (auto Error = Rule.defineComplexSubOperand( SubOperand.getName(), SrcChild.getOperator(), RendererID, I, SrcChildName)) return Error; } } return Error::success(); } auto MaybeInsnOperand = OM.addPredicate( InsnMatcher.getRuleMatcher(), SrcChild.getName()); if (!MaybeInsnOperand) { // This isn't strictly true. If the user were to provide exactly the same // matchers as the original operand then we could allow it. However, it's // simpler to not permit the redundant specification. return failedImport( "Nested instruction cannot be the same as another operand"); } // Map the node to a gMIR instruction. InstructionOperandMatcher &InsnOperand = **MaybeInsnOperand; auto InsnMatcherOrError = createAndImportSelDAGMatcher( Rule, InsnOperand.getInsnMatcher(), SrcChild, TempOpIdx); if (auto Error = InsnMatcherOrError.takeError()) return Error; return Error::success(); } if (SrcChild.hasAnyPredicate()) return failedImport("Src pattern child has unsupported predicate"); // Check for constant immediates. if (auto *ChildInt = dyn_cast(SrcChild.getLeafValue())) { if (OperandIsImmArg) { // Checks for argument directly in operand list OM.addPredicate(ChildInt->getValue()); } else { // Checks for materialized constant OM.addPredicate(ChildInt->getValue()); } return Error::success(); } // Check for def's like register classes or ComplexPattern's. if (auto *ChildDefInit = dyn_cast(SrcChild.getLeafValue())) { auto *ChildRec = ChildDefInit->getDef(); // Check for register classes. if (ChildRec->isSubClassOf("RegisterClass") || ChildRec->isSubClassOf("RegisterOperand")) { OM.addPredicate( Target.getRegisterClass(getInitValueAsRegClass(ChildDefInit))); return Error::success(); } if (ChildRec->isSubClassOf("Register")) { // This just be emitted as a copy to the specific register. ValueTypeByHwMode VT = ChildTypes.front().getValueTypeByHwMode(); const CodeGenRegisterClass *RC = CGRegs.getMinimalPhysRegClass(ChildRec, &VT); if (!RC) { return failedImport( "Could not determine physical register class of pattern source"); } OM.addPredicate(*RC); return Error::success(); } // Check for ValueType. if (ChildRec->isSubClassOf("ValueType")) { // We already added a type check as standard practice so this doesn't need // to do anything. return Error::success(); } // Check for ComplexPattern's. if (ChildRec->isSubClassOf("ComplexPattern")) return importComplexPatternOperandMatcher(OM, ChildRec, TempOpIdx); if (ChildRec->isSubClassOf("ImmLeaf")) { return failedImport( "Src pattern child def is an unsupported tablegen class (ImmLeaf)"); } // Place holder for SRCVALUE nodes. Nothing to do here. if (ChildRec->getName() == "srcvalue") return Error::success(); const bool ImmAllOnesV = ChildRec->getName() == "immAllOnesV"; if (ImmAllOnesV || ChildRec->getName() == "immAllZerosV") { auto MaybeInsnOperand = OM.addPredicate( InsnMatcher.getRuleMatcher(), SrcChild.getName(), false); InstructionOperandMatcher &InsnOperand = **MaybeInsnOperand; ValueTypeByHwMode VTy = ChildTypes.front().getValueTypeByHwMode(); const CodeGenInstruction &BuildVector = Target.getInstruction(RK.getDef("G_BUILD_VECTOR")); const CodeGenInstruction &BuildVectorTrunc = Target.getInstruction(RK.getDef("G_BUILD_VECTOR_TRUNC")); // Treat G_BUILD_VECTOR as the canonical opcode, and G_BUILD_VECTOR_TRUNC // as an alternative. InsnOperand.getInsnMatcher().addPredicate( ArrayRef({&BuildVector, &BuildVectorTrunc})); // TODO: Handle both G_BUILD_VECTOR and G_BUILD_VECTOR_TRUNC We could // theoretically not emit any opcode check, but getOpcodeMatcher currently // has to succeed. OperandMatcher &OM = InsnOperand.getInsnMatcher().addOperand(0, "", TempOpIdx); if (auto Error = OM.addTypeCheckPredicate(VTy, false /* OperandIsAPointer */)) return failedImport(toString(std::move(Error)) + " for result of Src pattern operator"); InsnOperand.getInsnMatcher().addPredicate( ImmAllOnesV ? VectorSplatImmPredicateMatcher::AllOnes : VectorSplatImmPredicateMatcher::AllZeros); return Error::success(); } return failedImport( "Src pattern child def is an unsupported tablegen class"); } return failedImport("Src pattern child is an unsupported kind"); } Expected GlobalISelEmitter::importExplicitUseRenderer( action_iterator InsertPt, RuleMatcher &Rule, BuildMIAction &DstMIBuilder, const TreePatternNode &DstChild, const TreePatternNode &Src) { const auto &SubOperand = Rule.getComplexSubOperand(DstChild.getName()); if (SubOperand) { DstMIBuilder.addRenderer( *std::get<0>(*SubOperand), DstChild.getName(), std::get<1>(*SubOperand), std::get<2>(*SubOperand)); return InsertPt; } if (!DstChild.isLeaf()) { if (DstChild.getOperator()->isSubClassOf("SDNodeXForm")) { auto &Child = DstChild.getChild(0); auto I = SDNodeXFormEquivs.find(DstChild.getOperator()); if (I != SDNodeXFormEquivs.end()) { Record *XFormOpc = DstChild.getOperator()->getValueAsDef("Opcode"); if (XFormOpc->getName() == "timm") { // If this is a TargetConstant, there won't be a corresponding // instruction to transform. Instead, this will refer directly to an // operand in an instruction's operand list. DstMIBuilder.addRenderer(*I->second, Child.getName()); } else { DstMIBuilder.addRenderer(*I->second, Child.getName()); } return InsertPt; } return failedImport("SDNodeXForm " + Child.getName() + " has no custom renderer"); } // We accept 'bb' here. It's an operator because BasicBlockSDNode isn't // inline, but in MI it's just another operand. if (DstChild.getOperator()->isSubClassOf("SDNode")) { auto &ChildSDNI = CGP.getSDNodeInfo(DstChild.getOperator()); if (ChildSDNI.getSDClassName() == "BasicBlockSDNode") { DstMIBuilder.addRenderer(DstChild.getName()); return InsertPt; } } // Similarly, imm is an operator in TreePatternNode's view but must be // rendered as operands. // FIXME: The target should be able to choose sign-extended when appropriate // (e.g. on Mips). if (DstChild.getOperator()->getName() == "timm") { DstMIBuilder.addRenderer(DstChild.getName()); return InsertPt; } if (DstChild.getOperator()->getName() == "tframeindex") { DstMIBuilder.addRenderer(DstChild.getName()); return InsertPt; } if (DstChild.getOperator()->getName() == "imm") { DstMIBuilder.addRenderer(DstChild.getName()); return InsertPt; } if (DstChild.getOperator()->getName() == "fpimm") { DstMIBuilder.addRenderer( DstChild.getName()); return InsertPt; } if (DstChild.getOperator()->isSubClassOf("Instruction")) { auto OpTy = getInstResultType(DstChild, Target); if (!OpTy) return OpTy.takeError(); unsigned TempRegID = Rule.allocateTempRegID(); InsertPt = Rule.insertAction(InsertPt, *OpTy, TempRegID); DstMIBuilder.addRenderer(TempRegID); auto InsertPtOrError = createAndImportSubInstructionRenderer( ++InsertPt, Rule, DstChild, Src, TempRegID); if (auto Error = InsertPtOrError.takeError()) return std::move(Error); return InsertPtOrError.get(); } return failedImport("Dst pattern child isn't a leaf node or an MBB" + llvm::to_string(DstChild)); } // It could be a specific immediate in which case we should just check for // that immediate. if (const IntInit *ChildIntInit = dyn_cast(DstChild.getLeafValue())) { DstMIBuilder.addRenderer(ChildIntInit->getValue()); return InsertPt; } // Otherwise, we're looking for a bog-standard RegisterClass operand. if (auto *ChildDefInit = dyn_cast(DstChild.getLeafValue())) { auto *ChildRec = ChildDefInit->getDef(); ArrayRef ChildTypes = DstChild.getExtTypes(); if (ChildTypes.size() != 1) return failedImport("Dst pattern child has multiple results"); std::optional OpTyOrNone; if (ChildTypes.front().isMachineValueType()) OpTyOrNone = MVTToLLT(ChildTypes.front().getMachineValueType().SimpleTy); if (!OpTyOrNone) return failedImport("Dst operand has an unsupported type"); if (ChildRec->isSubClassOf("Register")) { DstMIBuilder.addRenderer(Target, ChildRec); return InsertPt; } if (ChildRec->isSubClassOf("RegisterClass") || ChildRec->isSubClassOf("RegisterOperand") || ChildRec->isSubClassOf("ValueType")) { if (ChildRec->isSubClassOf("RegisterOperand") && !ChildRec->isValueUnset("GIZeroRegister")) { DstMIBuilder.addRenderer( DstChild.getName(), ChildRec->getValueAsDef("GIZeroRegister")); return InsertPt; } DstMIBuilder.addRenderer(DstChild.getName()); return InsertPt; } if (ChildRec->isSubClassOf("SubRegIndex")) { CodeGenSubRegIndex *SubIdx = CGRegs.getSubRegIdx(ChildRec); DstMIBuilder.addRenderer(SubIdx->EnumValue); return InsertPt; } if (ChildRec->isSubClassOf("ComplexPattern")) { const auto &ComplexPattern = ComplexPatternEquivs.find(ChildRec); if (ComplexPattern == ComplexPatternEquivs.end()) return failedImport( "SelectionDAG ComplexPattern not mapped to GlobalISel"); const OperandMatcher &OM = Rule.getOperandMatcher(DstChild.getName()); DstMIBuilder.addRenderer( *ComplexPattern->second, DstChild.getName(), OM.getAllocatedTemporariesBaseID()); return InsertPt; } return failedImport( "Dst pattern child def is an unsupported tablegen class"); } // Handle the case where the MVT/register class is omitted in the dest pattern // but MVT exists in the source pattern. if (isa(DstChild.getLeafValue())) { for (unsigned NumOp = 0; NumOp < Src.getNumChildren(); NumOp++) if (Src.getChild(NumOp).getName() == DstChild.getName()) { DstMIBuilder.addRenderer(Src.getChild(NumOp).getName()); return InsertPt; } } return failedImport("Dst pattern child is an unsupported kind"); } Expected GlobalISelEmitter::createAndImportInstructionRenderer( RuleMatcher &M, InstructionMatcher &InsnMatcher, const TreePatternNode &Src, const TreePatternNode &Dst) { auto InsertPtOrError = createInstructionRenderer(M.actions_end(), M, Dst); if (auto Error = InsertPtOrError.takeError()) return std::move(Error); action_iterator InsertPt = InsertPtOrError.get(); BuildMIAction &DstMIBuilder = *static_cast(InsertPt->get()); for (auto PhysInput : InsnMatcher.getPhysRegInputs()) { InsertPt = M.insertAction( InsertPt, M.allocateOutputInsnID(), &Target.getInstruction(RK.getDef("COPY"))); BuildMIAction &CopyToPhysRegMIBuilder = *static_cast(InsertPt->get()); CopyToPhysRegMIBuilder.addRenderer( Target, PhysInput.first, true); CopyToPhysRegMIBuilder.addRenderer(PhysInput.first); } if (auto Error = importExplicitDefRenderers(InsertPt, M, DstMIBuilder, Src, Dst) .takeError()) return std::move(Error); if (auto Error = importExplicitUseRenderers(InsertPt, M, DstMIBuilder, Dst, Src) .takeError()) return std::move(Error); return DstMIBuilder; } Expected GlobalISelEmitter::createAndImportSubInstructionRenderer( const action_iterator InsertPt, RuleMatcher &M, const TreePatternNode &Dst, const TreePatternNode &Src, unsigned TempRegID) { auto InsertPtOrError = createInstructionRenderer(InsertPt, M, Dst); // TODO: Assert there's exactly one result. if (auto Error = InsertPtOrError.takeError()) return std::move(Error); BuildMIAction &DstMIBuilder = *static_cast(InsertPtOrError.get()->get()); // Assign the result to TempReg. DstMIBuilder.addRenderer(TempRegID, true); // Handle additional (ignored) results. if (DstMIBuilder.getCGI()->Operands.NumDefs > 1) { InsertPtOrError = importExplicitDefRenderers( std::prev(*InsertPtOrError), M, DstMIBuilder, Src, Dst, /*Start=*/1); if (auto Error = InsertPtOrError.takeError()) return std::move(Error); } InsertPtOrError = importExplicitUseRenderers(InsertPtOrError.get(), M, DstMIBuilder, Dst, Src); if (auto Error = InsertPtOrError.takeError()) return std::move(Error); // We need to make sure that when we import an INSERT_SUBREG as a // subinstruction that it ends up being constrained to the correct super // register and subregister classes. auto OpName = Target.getInstruction(Dst.getOperator()).TheDef->getName(); if (OpName == "INSERT_SUBREG") { auto SubClass = inferRegClassFromPattern(Dst.getChild(1)); if (!SubClass) return failedImport( "Cannot infer register class from INSERT_SUBREG operand #1"); std::optional SuperClass = inferSuperRegisterClassForNode(Dst.getExtType(0), Dst.getChild(0), Dst.getChild(2)); if (!SuperClass) return failedImport( "Cannot infer register class for INSERT_SUBREG operand #0"); // The destination and the super register source of an INSERT_SUBREG must // be the same register class. M.insertAction( InsertPt, DstMIBuilder.getInsnID(), 0, **SuperClass); M.insertAction( InsertPt, DstMIBuilder.getInsnID(), 1, **SuperClass); M.insertAction( InsertPt, DstMIBuilder.getInsnID(), 2, **SubClass); return InsertPtOrError.get(); } if (OpName == "EXTRACT_SUBREG") { // EXTRACT_SUBREG selects into a subregister COPY but unlike most // instructions, the result register class is controlled by the // subregisters of the operand. As a result, we must constrain the result // class rather than check that it's already the right one. auto SuperClass = inferRegClassFromPattern(Dst.getChild(0)); if (!SuperClass) return failedImport( "Cannot infer register class from EXTRACT_SUBREG operand #0"); auto SubIdx = inferSubRegIndexForNode(Dst.getChild(1)); if (!SubIdx) return failedImport("EXTRACT_SUBREG child #1 is not a subreg index"); const auto SrcRCDstRCPair = (*SuperClass)->getMatchingSubClassWithSubRegs(CGRegs, *SubIdx); assert(SrcRCDstRCPair->second && "Couldn't find a matching subclass"); M.insertAction( InsertPt, DstMIBuilder.getInsnID(), 0, *SrcRCDstRCPair->second); M.insertAction( InsertPt, DstMIBuilder.getInsnID(), 1, *SrcRCDstRCPair->first); // We're done with this pattern! It's eligible for GISel emission; return // it. return InsertPtOrError.get(); } // Similar to INSERT_SUBREG, we also have to handle SUBREG_TO_REG as a // subinstruction. if (OpName == "SUBREG_TO_REG") { auto SubClass = inferRegClassFromPattern(Dst.getChild(1)); if (!SubClass) return failedImport( "Cannot infer register class from SUBREG_TO_REG child #1"); auto SuperClass = inferSuperRegisterClass(Dst.getExtType(0), Dst.getChild(2)); if (!SuperClass) return failedImport( "Cannot infer register class for SUBREG_TO_REG operand #0"); M.insertAction( InsertPt, DstMIBuilder.getInsnID(), 0, **SuperClass); M.insertAction( InsertPt, DstMIBuilder.getInsnID(), 2, **SubClass); return InsertPtOrError.get(); } if (OpName == "REG_SEQUENCE") { auto SuperClass = inferRegClassFromPattern(Dst.getChild(0)); M.insertAction( InsertPt, DstMIBuilder.getInsnID(), 0, **SuperClass); unsigned Num = Dst.getNumChildren(); for (unsigned I = 1; I != Num; I += 2) { const TreePatternNode &SubRegChild = Dst.getChild(I + 1); auto SubIdx = inferSubRegIndexForNode(SubRegChild); if (!SubIdx) return failedImport("REG_SEQUENCE child is not a subreg index"); const auto SrcRCDstRCPair = (*SuperClass)->getMatchingSubClassWithSubRegs(CGRegs, *SubIdx); assert(SrcRCDstRCPair->second && "Couldn't find a matching subclass"); M.insertAction( InsertPt, DstMIBuilder.getInsnID(), I, *SrcRCDstRCPair->second); } return InsertPtOrError.get(); } M.insertAction(InsertPt, DstMIBuilder.getInsnID()); return InsertPtOrError.get(); } Expected GlobalISelEmitter::createInstructionRenderer( action_iterator InsertPt, RuleMatcher &M, const TreePatternNode &Dst) { Record *DstOp = Dst.getOperator(); if (!DstOp->isSubClassOf("Instruction")) { if (DstOp->isSubClassOf("ValueType")) return failedImport( "Pattern operator isn't an instruction (it's a ValueType)"); return failedImport("Pattern operator isn't an instruction"); } CodeGenInstruction *DstI = &Target.getInstruction(DstOp); // COPY_TO_REGCLASS is just a copy with a ConstrainOperandToRegClassAction // attached. Similarly for EXTRACT_SUBREG except that's a subregister copy. StringRef Name = DstI->TheDef->getName(); if (Name == "COPY_TO_REGCLASS" || Name == "EXTRACT_SUBREG") DstI = &Target.getInstruction(RK.getDef("COPY")); return M.insertAction(InsertPt, M.allocateOutputInsnID(), DstI); } Expected GlobalISelEmitter::importExplicitDefRenderers( action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder, const TreePatternNode &Src, const TreePatternNode &Dst, unsigned Start) { const CodeGenInstruction *DstI = DstMIBuilder.getCGI(); const unsigned SrcNumDefs = Src.getExtTypes().size(); const unsigned DstNumDefs = DstI->Operands.NumDefs; if (DstNumDefs == 0) return InsertPt; for (unsigned I = Start; I < SrcNumDefs; ++I) { std::string OpName = getMangledRootDefName(DstI->Operands[I].Name); // CopyRenderer saves a StringRef, so cannot pass OpName itself - // let's use a string with an appropriate lifetime. StringRef PermanentRef = M.getOperandMatcher(OpName).getSymbolicName(); DstMIBuilder.addRenderer(PermanentRef); } // Some instructions have multiple defs, but are missing a type entry // (e.g. s_cc_out operands). if (Dst.getExtTypes().size() < DstNumDefs) return failedImport("unhandled discarded def"); for (unsigned I = SrcNumDefs; I < DstNumDefs; ++I) { const TypeSetByHwMode &ExtTy = Dst.getExtType(I); if (!ExtTy.isMachineValueType()) return failedImport("unsupported typeset"); auto OpTy = MVTToLLT(ExtTy.getMachineValueType().SimpleTy); if (!OpTy) return failedImport("unsupported type"); unsigned TempRegID = M.allocateTempRegID(); InsertPt = M.insertAction(InsertPt, *OpTy, TempRegID); DstMIBuilder.addRenderer(TempRegID, true, nullptr, true); } return InsertPt; } Expected GlobalISelEmitter::importExplicitUseRenderers( action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder, const llvm::TreePatternNode &Dst, const llvm::TreePatternNode &Src) { const CodeGenInstruction *DstI = DstMIBuilder.getCGI(); CodeGenInstruction *OrigDstI = &Target.getInstruction(Dst.getOperator()); StringRef Name = OrigDstI->TheDef->getName(); unsigned ExpectedDstINumUses = Dst.getNumChildren(); // EXTRACT_SUBREG needs to use a subregister COPY. if (Name == "EXTRACT_SUBREG") { if (!Dst.getChild(1).isLeaf()) return failedImport("EXTRACT_SUBREG child #1 is not a leaf"); DefInit *SubRegInit = dyn_cast(Dst.getChild(1).getLeafValue()); if (!SubRegInit) return failedImport("EXTRACT_SUBREG child #1 is not a subreg index"); CodeGenSubRegIndex *SubIdx = CGRegs.getSubRegIdx(SubRegInit->getDef()); const TreePatternNode &ValChild = Dst.getChild(0); if (!ValChild.isLeaf()) { // We really have to handle the source instruction, and then insert a // copy from the subregister. auto ExtractSrcTy = getInstResultType(ValChild, Target); if (!ExtractSrcTy) return ExtractSrcTy.takeError(); unsigned TempRegID = M.allocateTempRegID(); InsertPt = M.insertAction(InsertPt, *ExtractSrcTy, TempRegID); auto InsertPtOrError = createAndImportSubInstructionRenderer( ++InsertPt, M, ValChild, Src, TempRegID); if (auto Error = InsertPtOrError.takeError()) return std::move(Error); DstMIBuilder.addRenderer(TempRegID, false, SubIdx); return InsertPt; } // If this is a source operand, this is just a subregister copy. Record *RCDef = getInitValueAsRegClass(ValChild.getLeafValue()); if (!RCDef) return failedImport("EXTRACT_SUBREG child #0 could not " "be coerced to a register class"); CodeGenRegisterClass *RC = CGRegs.getRegClass(RCDef); const auto SrcRCDstRCPair = RC->getMatchingSubClassWithSubRegs(CGRegs, SubIdx); if (SrcRCDstRCPair) { assert(SrcRCDstRCPair->second && "Couldn't find a matching subclass"); if (SrcRCDstRCPair->first != RC) return failedImport("EXTRACT_SUBREG requires an additional COPY"); } StringRef RegOperandName = Dst.getChild(0).getName(); if (const auto &SubOperand = M.getComplexSubOperand(RegOperandName)) { DstMIBuilder.addRenderer( *std::get<0>(*SubOperand), RegOperandName, std::get<1>(*SubOperand), std::get<2>(*SubOperand), SubIdx); return InsertPt; } DstMIBuilder.addRenderer(RegOperandName, SubIdx); return InsertPt; } if (Name == "REG_SEQUENCE") { if (!Dst.getChild(0).isLeaf()) return failedImport("REG_SEQUENCE child #0 is not a leaf"); Record *RCDef = getInitValueAsRegClass(Dst.getChild(0).getLeafValue()); if (!RCDef) return failedImport("REG_SEQUENCE child #0 could not " "be coerced to a register class"); if ((ExpectedDstINumUses - 1) % 2 != 0) return failedImport("Malformed REG_SEQUENCE"); for (unsigned I = 1; I != ExpectedDstINumUses; I += 2) { const TreePatternNode &ValChild = Dst.getChild(I); const TreePatternNode &SubRegChild = Dst.getChild(I + 1); if (DefInit *SubRegInit = dyn_cast(SubRegChild.getLeafValue())) { CodeGenSubRegIndex *SubIdx = CGRegs.getSubRegIdx(SubRegInit->getDef()); auto InsertPtOrError = importExplicitUseRenderer(InsertPt, M, DstMIBuilder, ValChild, Src); if (auto Error = InsertPtOrError.takeError()) return std::move(Error); InsertPt = InsertPtOrError.get(); DstMIBuilder.addRenderer(SubIdx); } } return InsertPt; } // Render the explicit uses. unsigned DstINumUses = OrigDstI->Operands.size() - OrigDstI->Operands.NumDefs; if (Name == "COPY_TO_REGCLASS") { DstINumUses--; // Ignore the class constraint. ExpectedDstINumUses--; } // NumResults - This is the number of results produced by the instruction in // the "outs" list. unsigned NumResults = OrigDstI->Operands.NumDefs; // Number of operands we know the output instruction must have. If it is // variadic, we could have more operands. unsigned NumFixedOperands = DstI->Operands.size(); // Loop over all of the fixed operands of the instruction pattern, emitting // code to fill them all in. The node 'N' usually has number children equal to // the number of input operands of the instruction. However, in cases where // there are predicate operands for an instruction, we need to fill in the // 'execute always' values. Match up the node operands to the instruction // operands to do this. unsigned Child = 0; // Similarly to the code in TreePatternNode::ApplyTypeConstraints, count the // number of operands at the end of the list which have default values. // Those can come from the pattern if it provides enough arguments, or be // filled in with the default if the pattern hasn't provided them. But any // operand with a default value _before_ the last mandatory one will be // filled in with their defaults unconditionally. unsigned NonOverridableOperands = NumFixedOperands; while (NonOverridableOperands > NumResults && CGP.operandHasDefault(DstI->Operands[NonOverridableOperands - 1].Rec)) --NonOverridableOperands; unsigned NumDefaultOps = 0; for (unsigned I = 0; I != DstINumUses; ++I) { unsigned InstOpNo = DstI->Operands.NumDefs + I; // Determine what to emit for this operand. Record *OperandNode = DstI->Operands[InstOpNo].Rec; // If the operand has default values, introduce them now. if (CGP.operandHasDefault(OperandNode) && (InstOpNo < NonOverridableOperands || Child >= Dst.getNumChildren())) { // This is a predicate or optional def operand which the pattern has not // overridden, or which we aren't letting it override; emit the 'default // ops' operands. Record *OperandNode = DstI->Operands[InstOpNo].Rec; if (auto Error = importDefaultOperandRenderers( InsertPt, M, DstMIBuilder, CGP.getDefaultOperand(OperandNode))) return std::move(Error); ++NumDefaultOps; continue; } auto InsertPtOrError = importExplicitUseRenderer(InsertPt, M, DstMIBuilder, Dst.getChild(Child), Src); if (auto Error = InsertPtOrError.takeError()) return std::move(Error); InsertPt = InsertPtOrError.get(); ++Child; } if (NumDefaultOps + ExpectedDstINumUses != DstINumUses) return failedImport("Expected " + llvm::to_string(DstINumUses) + " used operands but found " + llvm::to_string(ExpectedDstINumUses) + " explicit ones and " + llvm::to_string(NumDefaultOps) + " default ones"); return InsertPt; } Error GlobalISelEmitter::importDefaultOperandRenderers( action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder, const DAGDefaultOperand &DefaultOp) const { for (const auto &Op : DefaultOp.DefaultOps) { const auto &N = *Op; if (!N.isLeaf()) return failedImport("Could not add default op"); const auto *DefaultOp = N.getLeafValue(); if (const DefInit *DefaultDefOp = dyn_cast(DefaultOp)) { std::optional OpTyOrNone = MVTToLLT(N.getSimpleType(0)); auto *Def = DefaultDefOp->getDef(); if (Def->getName() == "undef_tied_input") { unsigned TempRegID = M.allocateTempRegID(); M.insertAction(InsertPt, *OpTyOrNone, TempRegID); InsertPt = M.insertAction( InsertPt, M.allocateOutputInsnID(), &Target.getInstruction(RK.getDef("IMPLICIT_DEF"))); BuildMIAction &IDMIBuilder = *static_cast(InsertPt->get()); IDMIBuilder.addRenderer(TempRegID); DstMIBuilder.addRenderer(TempRegID); } else { DstMIBuilder.addRenderer(Target, Def); } continue; } if (const IntInit *DefaultIntOp = dyn_cast(DefaultOp)) { DstMIBuilder.addRenderer(DefaultIntOp->getValue()); continue; } return failedImport("Could not add default op"); } return Error::success(); } Error GlobalISelEmitter::importImplicitDefRenderers( BuildMIAction &DstMIBuilder, const std::vector &ImplicitDefs) const { if (!ImplicitDefs.empty()) return failedImport("Pattern defines a physical register"); return Error::success(); } std::optional GlobalISelEmitter::getRegClassFromLeaf(const TreePatternNode &Leaf) { assert(Leaf.isLeaf() && "Expected leaf?"); Record *RCRec = getInitValueAsRegClass(Leaf.getLeafValue()); if (!RCRec) return std::nullopt; CodeGenRegisterClass *RC = CGRegs.getRegClass(RCRec); if (!RC) return std::nullopt; return RC; } std::optional GlobalISelEmitter::inferRegClassFromPattern(const TreePatternNode &N) { if (N.isLeaf()) return getRegClassFromLeaf(N); // We don't have a leaf node, so we have to try and infer something. Check // that we have an instruction that we can infer something from. // Only handle things that produce at least one value (if multiple values, // just take the first one). if (N.getNumTypes() < 1) return std::nullopt; Record *OpRec = N.getOperator(); // We only want instructions. if (!OpRec->isSubClassOf("Instruction")) return std::nullopt; // Don't want to try and infer things when there could potentially be more // than one candidate register class. auto &Inst = Target.getInstruction(OpRec); // Handle any special-case instructions which we can safely infer register // classes from. StringRef InstName = Inst.TheDef->getName(); bool IsRegSequence = InstName == "REG_SEQUENCE"; if (IsRegSequence || InstName == "COPY_TO_REGCLASS") { // If we have a COPY_TO_REGCLASS, then we need to handle it specially. It // has the desired register class as the first child. const TreePatternNode &RCChild = N.getChild(IsRegSequence ? 0 : 1); if (!RCChild.isLeaf()) return std::nullopt; return getRegClassFromLeaf(RCChild); } if (InstName == "INSERT_SUBREG") { const TreePatternNode &Child0 = N.getChild(0); assert(Child0.getNumTypes() == 1 && "Unexpected number of types!"); const TypeSetByHwMode &VTy = Child0.getExtType(0); return inferSuperRegisterClassForNode(VTy, Child0, N.getChild(2)); } if (InstName == "EXTRACT_SUBREG") { assert(N.getNumTypes() == 1 && "Unexpected number of types!"); const TypeSetByHwMode &VTy = N.getExtType(0); return inferSuperRegisterClass(VTy, N.getChild(1)); } // Handle destination record types that we can safely infer a register class // from. const auto &DstIOperand = Inst.Operands[0]; Record *DstIOpRec = DstIOperand.Rec; if (DstIOpRec->isSubClassOf("RegisterOperand")) { DstIOpRec = DstIOpRec->getValueAsDef("RegClass"); const CodeGenRegisterClass &RC = Target.getRegisterClass(DstIOpRec); return &RC; } if (DstIOpRec->isSubClassOf("RegisterClass")) { const CodeGenRegisterClass &RC = Target.getRegisterClass(DstIOpRec); return &RC; } return std::nullopt; } std::optional GlobalISelEmitter::inferSuperRegisterClass( const TypeSetByHwMode &Ty, const TreePatternNode &SubRegIdxNode) { // We need a ValueTypeByHwMode for getSuperRegForSubReg. if (!Ty.isValueTypeByHwMode(false)) return std::nullopt; if (!SubRegIdxNode.isLeaf()) return std::nullopt; DefInit *SubRegInit = dyn_cast(SubRegIdxNode.getLeafValue()); if (!SubRegInit) return std::nullopt; CodeGenSubRegIndex *SubIdx = CGRegs.getSubRegIdx(SubRegInit->getDef()); // Use the information we found above to find a minimal register class which // supports the subregister and type we want. auto RC = Target.getSuperRegForSubReg(Ty.getValueTypeByHwMode(), CGRegs, SubIdx, /* MustBeAllocatable */ true); if (!RC) return std::nullopt; return *RC; } std::optional GlobalISelEmitter::inferSuperRegisterClassForNode( const TypeSetByHwMode &Ty, const TreePatternNode &SuperRegNode, const TreePatternNode &SubRegIdxNode) { // Check if we already have a defined register class for the super register // node. If we do, then we should preserve that rather than inferring anything // from the subregister index node. We can assume that whoever wrote the // pattern in the first place made sure that the super register and // subregister are compatible. if (std::optional SuperRegisterClass = inferRegClassFromPattern(SuperRegNode)) return *SuperRegisterClass; return inferSuperRegisterClass(Ty, SubRegIdxNode); } std::optional GlobalISelEmitter::inferSubRegIndexForNode( const TreePatternNode &SubRegIdxNode) { if (!SubRegIdxNode.isLeaf()) return std::nullopt; DefInit *SubRegInit = dyn_cast(SubRegIdxNode.getLeafValue()); if (!SubRegInit) return std::nullopt; return CGRegs.getSubRegIdx(SubRegInit->getDef()); } Expected GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { // Keep track of the matchers and actions to emit. int Score = P.getPatternComplexity(CGP); RuleMatcher M(P.getSrcRecord()->getLoc()); RuleMatcherScores[M.getRuleID()] = Score; M.addAction(llvm::to_string(P.getSrcPattern()) + " => " + llvm::to_string(P.getDstPattern())); SmallVector Predicates; P.getPredicateRecords(Predicates); if (auto Error = importRulePredicates(M, Predicates)) return std::move(Error); if (!P.getHwModeFeatures().empty()) M.addHwModeIdx(declareHwModeCheck(P.getHwModeFeatures())); // Next, analyze the pattern operators. TreePatternNode &Src = P.getSrcPattern(); TreePatternNode &Dst = P.getDstPattern(); // If the root of either pattern isn't a simple operator, ignore it. if (auto Err = isTrivialOperatorNode(Dst)) return failedImport("Dst pattern root isn't a trivial operator (" + toString(std::move(Err)) + ")"); if (auto Err = isTrivialOperatorNode(Src)) return failedImport("Src pattern root isn't a trivial operator (" + toString(std::move(Err)) + ")"); // The different predicates and matchers created during // addInstructionMatcher use the RuleMatcher M to set up their // instruction ID (InsnVarID) that are going to be used when // M is going to be emitted. // However, the code doing the emission still relies on the IDs // returned during that process by the RuleMatcher when issuing // the recordInsn opcodes. // Because of that: // 1. The order in which we created the predicates // and such must be the same as the order in which we emit them, // and // 2. We need to reset the generation of the IDs in M somewhere between // addInstructionMatcher and emit // // FIXME: Long term, we don't want to have to rely on this implicit // naming being the same. One possible solution would be to have // explicit operator for operation capture and reference those. // The plus side is that it would expose opportunities to share // the capture accross rules. The downside is that it would // introduce a dependency between predicates (captures must happen // before their first use.) InstructionMatcher &InsnMatcherTemp = M.addInstructionMatcher(Src.getName()); unsigned TempOpIdx = 0; const auto SavedFlags = M.setGISelFlags(P.getSrcRecord()); auto InsnMatcherOrError = createAndImportSelDAGMatcher(M, InsnMatcherTemp, Src, TempOpIdx); if (auto Error = InsnMatcherOrError.takeError()) return std::move(Error); InstructionMatcher &InsnMatcher = InsnMatcherOrError.get(); if (Dst.isLeaf()) { Record *RCDef = getInitValueAsRegClass(Dst.getLeafValue()); if (RCDef) { const CodeGenRegisterClass &RC = Target.getRegisterClass(RCDef); // We need to replace the def and all its uses with the specified // operand. However, we must also insert COPY's wherever needed. // For now, emit a copy and let the register allocator clean up. auto &DstI = Target.getInstruction(RK.getDef("COPY")); const auto &DstIOperand = DstI.Operands[0]; OperandMatcher &OM0 = InsnMatcher.getOperand(0); OM0.setSymbolicName(DstIOperand.Name); M.defineOperand(OM0.getSymbolicName(), OM0); OM0.addPredicate(RC); auto &DstMIBuilder = M.addAction(M.allocateOutputInsnID(), &DstI); DstMIBuilder.addRenderer(DstIOperand.Name); DstMIBuilder.addRenderer(Dst.getName()); M.addAction(0, 0, RC); // Erase the root. unsigned RootInsnID = M.getInsnVarID(InsnMatcher); M.addAction(RootInsnID); // We're done with this pattern! It's eligible for GISel emission; return // it. ++NumPatternImported; return std::move(M); } return failedImport("Dst pattern root isn't a known leaf"); } // Start with the defined operands (i.e., the results of the root operator). Record *DstOp = Dst.getOperator(); if (!DstOp->isSubClassOf("Instruction")) return failedImport("Pattern operator isn't an instruction"); auto &DstI = Target.getInstruction(DstOp); StringRef DstIName = DstI.TheDef->getName(); unsigned DstNumDefs = DstI.Operands.NumDefs, SrcNumDefs = Src.getExtTypes().size(); if (DstNumDefs < SrcNumDefs) { if (DstNumDefs != 0) return failedImport("Src pattern result has more defs than dst MI (" + to_string(SrcNumDefs) + " def(s) vs " + to_string(DstNumDefs) + " def(s))"); bool FoundNoUsePred = false; for (const auto &Pred : InsnMatcher.predicates()) { if ((FoundNoUsePred = isa(Pred.get()))) break; } if (!FoundNoUsePred) return failedImport("Src pattern result has " + to_string(SrcNumDefs) + " def(s) without the HasNoUse predicate set to true " "but Dst MI has no def"); } // The root of the match also has constraints on the register bank so that it // matches the result instruction. unsigned OpIdx = 0; unsigned N = std::min(DstNumDefs, SrcNumDefs); for (unsigned I = 0; I < N; ++I) { const TypeSetByHwMode &VTy = Src.getExtType(I); const auto &DstIOperand = DstI.Operands[OpIdx]; PointerUnion MatchedRC = DstIOperand.Rec; if (DstIName == "COPY_TO_REGCLASS") { MatchedRC = getInitValueAsRegClass(Dst.getChild(1).getLeafValue()); if (MatchedRC.isNull()) return failedImport( "COPY_TO_REGCLASS operand #1 isn't a register class"); } else if (DstIName == "REG_SEQUENCE") { MatchedRC = getInitValueAsRegClass(Dst.getChild(0).getLeafValue()); if (MatchedRC.isNull()) return failedImport("REG_SEQUENCE operand #0 isn't a register class"); } else if (DstIName == "EXTRACT_SUBREG") { auto InferredClass = inferRegClassFromPattern(Dst.getChild(0)); if (!InferredClass) return failedImport( "Could not infer class for EXTRACT_SUBREG operand #0"); // We can assume that a subregister is in the same bank as it's super // register. MatchedRC = (*InferredClass)->getDef(); } else if (DstIName == "INSERT_SUBREG") { auto MaybeSuperClass = inferSuperRegisterClassForNode(VTy, Dst.getChild(0), Dst.getChild(2)); if (!MaybeSuperClass) return failedImport( "Cannot infer register class for INSERT_SUBREG operand #0"); // Move to the next pattern here, because the register class we found // doesn't necessarily have a record associated with it. So, we can't // set DstIOpRec using this. MatchedRC = *MaybeSuperClass; } else if (DstIName == "SUBREG_TO_REG") { auto MaybeRegClass = inferSuperRegisterClass(VTy, Dst.getChild(2)); if (!MaybeRegClass) return failedImport( "Cannot infer register class for SUBREG_TO_REG operand #0"); MatchedRC = *MaybeRegClass; } else if (MatchedRC.get()->isSubClassOf("RegisterOperand")) MatchedRC = MatchedRC.get()->getValueAsDef("RegClass"); else if (!MatchedRC.get()->isSubClassOf("RegisterClass")) return failedImport("Dst MI def isn't a register class" + to_string(Dst)); OperandMatcher &OM = InsnMatcher.getOperand(OpIdx); // The operand names declared in the DstI instruction are unrelated to // those used in pattern's source and destination DAGs, so mangle the // former to prevent implicitly adding unexpected // GIM_CheckIsSameOperand predicates by the defineOperand method. OM.setSymbolicName(getMangledRootDefName(DstIOperand.Name)); M.defineOperand(OM.getSymbolicName(), OM); if (MatchedRC.is()) MatchedRC = &Target.getRegisterClass(MatchedRC.get()); OM.addPredicate( *MatchedRC.get()); ++OpIdx; } auto DstMIBuilderOrError = createAndImportInstructionRenderer(M, InsnMatcher, Src, Dst); if (auto Error = DstMIBuilderOrError.takeError()) return std::move(Error); BuildMIAction &DstMIBuilder = DstMIBuilderOrError.get(); // Render the implicit defs. // These are only added to the root of the result. if (auto Error = importImplicitDefRenderers(DstMIBuilder, P.getDstRegs())) return std::move(Error); DstMIBuilder.chooseInsnToMutate(M); // Constrain the registers to classes. This is normally derived from the // emitted instruction but a few instructions require special handling. if (DstIName == "COPY_TO_REGCLASS") { // COPY_TO_REGCLASS does not provide operand constraints itself but the // result is constrained to the class given by the second child. Record *DstIOpRec = getInitValueAsRegClass(Dst.getChild(1).getLeafValue()); if (DstIOpRec == nullptr) return failedImport("COPY_TO_REGCLASS operand #1 isn't a register class"); M.addAction( 0, 0, Target.getRegisterClass(DstIOpRec)); } else if (DstIName == "EXTRACT_SUBREG") { auto SuperClass = inferRegClassFromPattern(Dst.getChild(0)); if (!SuperClass) return failedImport( "Cannot infer register class from EXTRACT_SUBREG operand #0"); auto SubIdx = inferSubRegIndexForNode(Dst.getChild(1)); if (!SubIdx) return failedImport("EXTRACT_SUBREG child #1 is not a subreg index"); // It would be nice to leave this constraint implicit but we're required // to pick a register class so constrain the result to a register class // that can hold the correct MVT. // // FIXME: This may introduce an extra copy if the chosen class doesn't // actually contain the subregisters. assert(Src.getExtTypes().size() == 1 && "Expected Src of EXTRACT_SUBREG to have one result type"); const auto SrcRCDstRCPair = (*SuperClass)->getMatchingSubClassWithSubRegs(CGRegs, *SubIdx); if (!SrcRCDstRCPair) { return failedImport("subreg index is incompatible " "with inferred reg class"); } assert(SrcRCDstRCPair->second && "Couldn't find a matching subclass"); M.addAction(0, 0, *SrcRCDstRCPair->second); M.addAction(0, 1, *SrcRCDstRCPair->first); } else if (DstIName == "INSERT_SUBREG") { assert(Src.getExtTypes().size() == 1 && "Expected Src of INSERT_SUBREG to have one result type"); // We need to constrain the destination, a super regsister source, and a // subregister source. auto SubClass = inferRegClassFromPattern(Dst.getChild(1)); if (!SubClass) return failedImport( "Cannot infer register class from INSERT_SUBREG operand #1"); auto SuperClass = inferSuperRegisterClassForNode( Src.getExtType(0), Dst.getChild(0), Dst.getChild(2)); if (!SuperClass) return failedImport( "Cannot infer register class for INSERT_SUBREG operand #0"); M.addAction(0, 0, **SuperClass); M.addAction(0, 1, **SuperClass); M.addAction(0, 2, **SubClass); } else if (DstIName == "SUBREG_TO_REG") { // We need to constrain the destination and subregister source. assert(Src.getExtTypes().size() == 1 && "Expected Src of SUBREG_TO_REG to have one result type"); // Attempt to infer the subregister source from the first child. If it has // an explicitly given register class, we'll use that. Otherwise, we will // fail. auto SubClass = inferRegClassFromPattern(Dst.getChild(1)); if (!SubClass) return failedImport( "Cannot infer register class from SUBREG_TO_REG child #1"); // We don't have a child to look at that might have a super register node. auto SuperClass = inferSuperRegisterClass(Src.getExtType(0), Dst.getChild(2)); if (!SuperClass) return failedImport( "Cannot infer register class for SUBREG_TO_REG operand #0"); M.addAction(0, 0, **SuperClass); M.addAction(0, 2, **SubClass); } else if (DstIName == "REG_SEQUENCE") { auto SuperClass = inferRegClassFromPattern(Dst.getChild(0)); M.addAction(0, 0, **SuperClass); unsigned Num = Dst.getNumChildren(); for (unsigned I = 1; I != Num; I += 2) { TreePatternNode &SubRegChild = Dst.getChild(I + 1); auto SubIdx = inferSubRegIndexForNode(SubRegChild); if (!SubIdx) return failedImport("REG_SEQUENCE child is not a subreg index"); const auto SrcRCDstRCPair = (*SuperClass)->getMatchingSubClassWithSubRegs(CGRegs, *SubIdx); M.addAction(0, I, *SrcRCDstRCPair->second); } } else { M.addAction(0); } // Erase the root. unsigned RootInsnID = M.getInsnVarID(InsnMatcher); M.addAction(RootInsnID); // We're done with this pattern! It's eligible for GISel emission; return it. ++NumPatternImported; return std::move(M); } MatchTable GlobalISelEmitter::buildMatchTable(MutableArrayRef Rules, bool Optimize, bool WithCoverage) { std::vector InputRules; for (Matcher &Rule : Rules) InputRules.push_back(&Rule); if (!Optimize) return MatchTable::buildTable(InputRules, WithCoverage); 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::tuple(OpcodeOrder[L->getOpcode()], L->getNumOperands()) < std::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); } void GlobalISelEmitter::emitAdditionalImpl(raw_ostream &OS) { OS << "bool " << getClassName() << "::selectImpl(MachineInstr &I, CodeGenCoverage " "&CoverageInfo) const {\n" << " const PredicateBitset AvailableFeatures = " "getAvailableFeatures();\n" << " MachineIRBuilder B(I);\n" << " State.MIs.clear();\n" << " State.MIs.push_back(&I);\n\n" << " if (executeMatchTable(*this, State, ExecInfo, B" << ", getMatchTable(), TII, MF->getRegInfo(), TRI, RBI, AvailableFeatures" << ", &CoverageInfo)) {\n" << " return true;\n" << " }\n\n" << " return false;\n" << "}\n\n"; } void GlobalISelEmitter::emitMIPredicateFns(raw_ostream &OS) { std::vector MatchedRecords; std::copy_if(AllPatFrags.begin(), AllPatFrags.end(), std::back_inserter(MatchedRecords), [&](Record *R) { return !R->getValueAsString("GISelPredicateCode").empty(); }); emitMIPredicateFnsImpl( OS, " const MachineFunction &MF = *MI.getParent()->getParent();\n" " const MachineRegisterInfo &MRI = MF.getRegInfo();\n" " const auto &Operands = State.RecordedOperands;\n" " (void)Operands;\n" " (void)MRI;", ArrayRef(MatchedRecords), &getPatFragPredicateEnumName, [&](Record *R) { return R->getValueAsString("GISelPredicateCode"); }, "PatFrag predicates."); } void GlobalISelEmitter::emitI64ImmPredicateFns(raw_ostream &OS) { std::vector MatchedRecords; std::copy_if(AllPatFrags.begin(), AllPatFrags.end(), std::back_inserter(MatchedRecords), [&](Record *R) { bool Unset; return !R->getValueAsString("ImmediateCode").empty() && !R->getValueAsBitOrUnset("IsAPFloat", Unset) && !R->getValueAsBit("IsAPInt"); }); emitImmPredicateFnsImpl( OS, "I64", "int64_t", ArrayRef(MatchedRecords), &getPatFragPredicateEnumName, [&](Record *R) { return R->getValueAsString("ImmediateCode"); }, "PatFrag predicates."); } void GlobalISelEmitter::emitAPFloatImmPredicateFns(raw_ostream &OS) { std::vector MatchedRecords; std::copy_if(AllPatFrags.begin(), AllPatFrags.end(), std::back_inserter(MatchedRecords), [&](Record *R) { bool Unset; return !R->getValueAsString("ImmediateCode").empty() && R->getValueAsBitOrUnset("IsAPFloat", Unset); }); emitImmPredicateFnsImpl( OS, "APFloat", "const APFloat &", ArrayRef(MatchedRecords), &getPatFragPredicateEnumName, [&](Record *R) { return R->getValueAsString("ImmediateCode"); }, "PatFrag predicates."); } void GlobalISelEmitter::emitAPIntImmPredicateFns(raw_ostream &OS) { std::vector MatchedRecords; std::copy_if(AllPatFrags.begin(), AllPatFrags.end(), std::back_inserter(MatchedRecords), [&](Record *R) { return !R->getValueAsString("ImmediateCode").empty() && R->getValueAsBit("IsAPInt"); }); emitImmPredicateFnsImpl( OS, "APInt", "const APInt &", ArrayRef(MatchedRecords), &getPatFragPredicateEnumName, [&](Record *R) { return R->getValueAsString("ImmediateCode"); }, "PatFrag predicates."); } void GlobalISelEmitter::emitTestSimplePredicate(raw_ostream &OS) { OS << "bool " << getClassName() << "::testSimplePredicate(unsigned) const {\n" << " llvm_unreachable(\"" + getClassName() + " does not support simple predicates!\");\n" << " return false;\n" << "}\n"; } void GlobalISelEmitter::emitRunCustomAction(raw_ostream &OS) { OS << "bool " << getClassName() << "::runCustomAction(unsigned, const MatcherState&, NewMIVector &) const " "{\n" << " llvm_unreachable(\"" + getClassName() + " does not support custom C++ actions!\");\n" << "}\n"; } void GlobalISelEmitter::postProcessRule(RuleMatcher &M) { SmallPtrSet UsedRegs; // TODO: deal with subregs? for (auto &A : M.actions()) { auto *MI = dyn_cast(A.get()); if (!MI) continue; for (auto *Use : MI->getCGI()->ImplicitUses) UsedRegs.insert(Use); } for (auto &A : M.actions()) { auto *MI = dyn_cast(A.get()); if (!MI) continue; for (auto *Def : MI->getCGI()->ImplicitDefs) { if (!UsedRegs.contains(Def)) MI->setDeadImplicitDef(Def); } } } void GlobalISelEmitter::run(raw_ostream &OS) { if (!UseCoverageFile.empty()) { RuleCoverage = CodeGenCoverage(); auto RuleCoverageBufOrErr = MemoryBuffer::getFile(UseCoverageFile); if (!RuleCoverageBufOrErr) { PrintWarning(SMLoc(), "Missing rule coverage data"); RuleCoverage = std::nullopt; } else { if (!RuleCoverage->parse(*RuleCoverageBufOrErr.get(), Target.getName())) { PrintWarning(SMLoc(), "Ignoring invalid or missing rule coverage data"); RuleCoverage = std::nullopt; } } } // Track the run-time opcode values gatherOpcodeValues(); // Track the run-time LLT ID values gatherTypeIDValues(); // Track the GINodeEquiv definitions. gatherNodeEquivs(); AllPatFrags = RK.getAllDerivedDefinitions("PatFrags"); emitSourceFileHeader( ("Global Instruction Selector for the " + Target.getName() + " target") .str(), OS); std::vector Rules; // Look through the SelectionDAG patterns we found, possibly emitting some. for (const PatternToMatch &Pat : CGP.ptms()) { ++NumPatternTotal; if (Pat.getGISelShouldIgnore()) continue; // skip without warning auto MatcherOrErr = runOnPattern(Pat); // The pattern analysis can fail, indicating an unsupported pattern. // Report that if we've been asked to do so. if (auto Err = MatcherOrErr.takeError()) { if (WarnOnSkippedPatterns) { PrintWarning(Pat.getSrcRecord()->getLoc(), "Skipped pattern: " + toString(std::move(Err))); } else { consumeError(std::move(Err)); } ++NumPatternImportsSkipped; continue; } if (RuleCoverage) { if (RuleCoverage->isCovered(MatcherOrErr->getRuleID())) ++NumPatternsTested; else PrintWarning(Pat.getSrcRecord()->getLoc(), "Pattern is not covered by a test"); } Rules.push_back(std::move(MatcherOrErr.get())); postProcessRule(Rules.back()); } // Comparison function to order records by name. auto OrderByName = [](const Record *A, const Record *B) { return A->getName() < B->getName(); }; std::vector ComplexPredicates = RK.getAllDerivedDefinitions("GIComplexOperandMatcher"); llvm::sort(ComplexPredicates, OrderByName); std::vector CustomRendererFns; transform(RK.getAllDerivedDefinitions("GICustomOperandRenderer"), std::back_inserter(CustomRendererFns), [](const auto &Record) { return Record->getValueAsString("RendererFn"); }); // Sort and remove duplicates to get a list of unique renderer functions, in // case some were mentioned more than once. llvm::sort(CustomRendererFns); CustomRendererFns.erase(llvm::unique(CustomRendererFns), CustomRendererFns.end()); // Create a table containing the LLT objects needed by the matcher and an enum // for the matcher to reference them with. std::vector TypeObjects; append_range(TypeObjects, KnownTypes); llvm::sort(TypeObjects); // Sort rules. llvm::stable_sort(Rules, [&](const RuleMatcher &A, const RuleMatcher &B) { int ScoreA = RuleMatcherScores[A.getRuleID()]; int ScoreB = RuleMatcherScores[B.getRuleID()]; if (ScoreA > ScoreB) return true; if (ScoreB > ScoreA) return false; if (A.isHigherPriorityThan(B)) { assert(!B.isHigherPriorityThan(A) && "Cannot be more important " "and less important at " "the same time"); return true; } return false; }); unsigned MaxTemporaries = 0; for (const auto &Rule : Rules) MaxTemporaries = std::max(MaxTemporaries, Rule.countRendererFns()); // Build match table const MatchTable Table = buildMatchTable(Rules, OptimizeMatchTable, GenerateCoverage); emitPredicateBitset(OS, "GET_GLOBALISEL_PREDICATE_BITSET"); emitTemporariesDecl(OS, "GET_GLOBALISEL_TEMPORARIES_DECL"); emitTemporariesInit(OS, MaxTemporaries, "GET_GLOBALISEL_TEMPORARIES_INIT"); emitExecutorImpl(OS, Table, TypeObjects, Rules, ComplexPredicates, CustomRendererFns, "GET_GLOBALISEL_IMPL"); emitPredicatesDecl(OS, "GET_GLOBALISEL_PREDICATES_DECL"); emitPredicatesInit(OS, "GET_GLOBALISEL_PREDICATES_INIT"); } void GlobalISelEmitter::declareSubtargetFeature(Record *Predicate) { SubtargetFeatures.try_emplace(Predicate, Predicate, SubtargetFeatures.size()); } unsigned GlobalISelEmitter::declareHwModeCheck(StringRef HwModeFeatures) { return HwModes.emplace(HwModeFeatures.str(), HwModes.size()).first->second; } } // end anonymous namespace //===----------------------------------------------------------------------===// static TableGen::Emitter::OptClass X("gen-global-isel", "Generate GlobalISel selector");