diff options
Diffstat (limited to 'llvm/utils/TableGen')
| -rw-r--r-- | llvm/utils/TableGen/CodeEmitterGen.cpp | 133 | ||||
| -rw-r--r-- | llvm/utils/TableGen/CodeGenDAGPatterns.cpp | 154 | ||||
| -rw-r--r-- | llvm/utils/TableGen/CodeGenDAGPatterns.h | 8 | ||||
| -rw-r--r-- | llvm/utils/TableGen/CodeGenRegisters.cpp | 20 | ||||
| -rw-r--r-- | llvm/utils/TableGen/CodeGenRegisters.h | 5 | ||||
| -rw-r--r-- | llvm/utils/TableGen/GlobalISelEmitter.cpp | 77 | ||||
| -rw-r--r-- | llvm/utils/TableGen/InstrInfoEmitter.cpp | 154 | ||||
| -rw-r--r-- | llvm/utils/TableGen/SubtargetFeatureInfo.cpp | 9 |
8 files changed, 350 insertions, 210 deletions
diff --git a/llvm/utils/TableGen/CodeEmitterGen.cpp b/llvm/utils/TableGen/CodeEmitterGen.cpp index 2b9931b23c11..1d00c3cfd069 100644 --- a/llvm/utils/TableGen/CodeEmitterGen.cpp +++ b/llvm/utils/TableGen/CodeEmitterGen.cpp @@ -332,14 +332,6 @@ std::string CodeEmitterGen::getInstructionCaseForEncoding(Record *R, Record *Enc return Case; } -static std::string -getNameForFeatureBitset(const std::vector<Record *> &FeatureBitset) { - std::string Name = "CEFBS"; - for (const auto &Feature : FeatureBitset) - Name += ("_" + Feature->getName()).str(); - return Name; -} - static void emitInstBits(raw_ostream &OS, const APInt &Bits) { for (unsigned I = 0; I < Bits.getNumWords(); ++I) OS << ((I > 0) ? ", " : "") << "UINT64_C(" << utostr(Bits.getRawData()[I]) @@ -530,131 +522,6 @@ void CodeEmitterGen::run(raw_ostream &o) { o << " return Value;\n"; o << "}\n\n"; } - - const auto &All = SubtargetFeatureInfo::getAll(Records); - std::map<Record *, SubtargetFeatureInfo, LessRecordByID> SubtargetFeatures; - SubtargetFeatures.insert(All.begin(), All.end()); - - o << "#ifdef ENABLE_INSTR_PREDICATE_VERIFIER\n" - << "#undef ENABLE_INSTR_PREDICATE_VERIFIER\n" - << "#include <sstream>\n\n"; - - // Emit the subtarget feature enumeration. - SubtargetFeatureInfo::emitSubtargetFeatureBitEnumeration(SubtargetFeatures, - o); - - // Emit the name table for error messages. - o << "#ifndef NDEBUG\n"; - SubtargetFeatureInfo::emitNameTable(SubtargetFeatures, o); - o << "#endif // NDEBUG\n"; - - // Emit the available features compute function. - SubtargetFeatureInfo::emitComputeAssemblerAvailableFeatures( - Target.getName(), "MCCodeEmitter", "computeAvailableFeatures", - SubtargetFeatures, o); - - std::vector<std::vector<Record *>> FeatureBitsets; - for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) { - FeatureBitsets.emplace_back(); - for (Record *Predicate : Inst->TheDef->getValueAsListOfDefs("Predicates")) { - const auto &I = SubtargetFeatures.find(Predicate); - if (I != SubtargetFeatures.end()) - FeatureBitsets.back().push_back(I->second.TheDef); - } - } - - llvm::sort(FeatureBitsets, [&](const std::vector<Record *> &A, - const std::vector<Record *> &B) { - if (A.size() < B.size()) - return true; - if (A.size() > B.size()) - return false; - for (auto Pair : zip(A, B)) { - if (std::get<0>(Pair)->getName() < std::get<1>(Pair)->getName()) - return true; - if (std::get<0>(Pair)->getName() > std::get<1>(Pair)->getName()) - return false; - } - return false; - }); - FeatureBitsets.erase( - std::unique(FeatureBitsets.begin(), FeatureBitsets.end()), - FeatureBitsets.end()); - o << "#ifndef NDEBUG\n" - << "// Feature bitsets.\n" - << "enum : " << getMinimalTypeForRange(FeatureBitsets.size()) << " {\n" - << " CEFBS_None,\n"; - for (const auto &FeatureBitset : FeatureBitsets) { - if (FeatureBitset.empty()) - continue; - o << " " << getNameForFeatureBitset(FeatureBitset) << ",\n"; - } - o << "};\n\n" - << "static constexpr FeatureBitset FeatureBitsets[] = {\n" - << " {}, // CEFBS_None\n"; - for (const auto &FeatureBitset : FeatureBitsets) { - if (FeatureBitset.empty()) - continue; - o << " {"; - for (const auto &Feature : FeatureBitset) { - const auto &I = SubtargetFeatures.find(Feature); - assert(I != SubtargetFeatures.end() && "Didn't import predicate?"); - o << I->second.getEnumBitName() << ", "; - } - o << "},\n"; - } - o << "};\n" - << "#endif // NDEBUG\n\n"; - - - // Emit the predicate verifier. - o << "void " << Target.getName() - << "MCCodeEmitter::verifyInstructionPredicates(\n" - << " const MCInst &Inst, const FeatureBitset &AvailableFeatures) const {\n" - << "#ifndef NDEBUG\n" - << " static " << getMinimalTypeForRange(FeatureBitsets.size()) - << " RequiredFeaturesRefs[] = {\n"; - unsigned InstIdx = 0; - for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) { - o << " CEFBS"; - unsigned NumPredicates = 0; - for (Record *Predicate : Inst->TheDef->getValueAsListOfDefs("Predicates")) { - const auto &I = SubtargetFeatures.find(Predicate); - if (I != SubtargetFeatures.end()) { - o << '_' << I->second.TheDef->getName(); - NumPredicates++; - } - } - if (!NumPredicates) - o << "_None"; - o << ", // " << Inst->TheDef->getName() << " = " << InstIdx << "\n"; - InstIdx++; - } - o << " };\n\n"; - o << " assert(Inst.getOpcode() < " << InstIdx << ");\n"; - o << " const FeatureBitset &RequiredFeatures = " - "FeatureBitsets[RequiredFeaturesRefs[Inst.getOpcode()]];\n"; - o << " FeatureBitset MissingFeatures =\n" - << " (AvailableFeatures & RequiredFeatures) ^\n" - << " RequiredFeatures;\n" - << " if (MissingFeatures.any()) {\n" - << " std::ostringstream Msg;\n" - << " Msg << \"Attempting to emit \" << " - "MCII.getName(Inst.getOpcode()).str()\n" - << " << \" instruction but the \";\n" - << " for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i)\n" - << " if (MissingFeatures.test(i))\n" - << " Msg << SubtargetFeatureNames[i] << \" \";\n" - << " Msg << \"predicate(s) are not met\";\n" - << " report_fatal_error(Msg.str().c_str());\n" - << " }\n" - << "#else\n" - << " // Silence unused variable warning on targets that don't use MCII for " - "other purposes (e.g. BPF).\n" - << " (void)MCII;\n" - << "#endif // NDEBUG\n"; - o << "}\n"; - o << "#endif\n"; } } // end anonymous namespace diff --git a/llvm/utils/TableGen/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/CodeGenDAGPatterns.cpp index 9d6adb6d2c37..c15728ac7d23 100644 --- a/llvm/utils/TableGen/CodeGenDAGPatterns.cpp +++ b/llvm/utils/TableGen/CodeGenDAGPatterns.cpp @@ -46,6 +46,9 @@ static inline bool isVector(MVT VT) { static inline bool isScalar(MVT VT) { return !VT.isVector(); } +static inline bool isScalarInteger(MVT VT) { + return VT.isScalarInteger(); +} template <typename Predicate> static bool berase_if(MachineValueTypeSet &S, Predicate P) { @@ -61,6 +64,17 @@ static bool berase_if(MachineValueTypeSet &S, Predicate P) { return Erased; } +void MachineValueTypeSet::writeToStream(raw_ostream &OS) const { + SmallVector<MVT, 4> Types(begin(), end()); + array_pod_sort(Types.begin(), Types.end()); + + OS << '['; + ListSeparator LS(" "); + for (const MVT &T : Types) + OS << LS << ValueTypeByHwMode::getMVTName(T); + OS << ']'; +} + // --- TypeSetByHwMode // This is a parameterized type-set class. For each mode there is a list @@ -193,22 +207,11 @@ void TypeSetByHwMode::writeToStream(raw_ostream &OS) const { OS << '{'; for (unsigned M : Modes) { OS << ' ' << getModeName(M) << ':'; - writeToStream(get(M), OS); + get(M).writeToStream(OS); } OS << " }"; } -void TypeSetByHwMode::writeToStream(const SetType &S, raw_ostream &OS) { - SmallVector<MVT, 4> Types(S.begin(), S.end()); - array_pod_sort(Types.begin(), Types.end()); - - OS << '['; - ListSeparator LS(" "); - for (const MVT &T : Types) - OS << LS << ValueTypeByHwMode::getMVTName(T); - OS << ']'; -} - bool TypeSetByHwMode::operator==(const TypeSetByHwMode &VTS) const { // The isSimple call is much quicker than hasDefault - check this first. bool IsSimple = isSimple(); @@ -253,6 +256,10 @@ bool TypeSetByHwMode::operator==(const TypeSetByHwMode &VTS) const { } namespace llvm { + raw_ostream &operator<<(raw_ostream &OS, const MachineValueTypeSet &T) { + T.writeToStream(OS); + return OS; + } raw_ostream &operator<<(raw_ostream &OS, const TypeSetByHwMode &T) { T.writeToStream(OS); return OS; @@ -266,10 +273,11 @@ void TypeSetByHwMode::dump() const { bool TypeSetByHwMode::intersect(SetType &Out, const SetType &In) { bool OutP = Out.count(MVT::iPTR), InP = In.count(MVT::iPTR); - auto Int = [&In](MVT T) -> bool { return !In.count(T); }; + // Complement of In. + auto CompIn = [&In](MVT T) -> bool { return !In.count(T); }; if (OutP == InP) - return berase_if(Out, Int); + return berase_if(Out, CompIn); // Compute the intersection of scalars separately to account for only // one set containing iPTR. @@ -285,42 +293,64 @@ bool TypeSetByHwMode::intersect(SetType &Out, const SetType &In) { // { iPTR i32 } * { i32 i64 } -> { i32 i64 } // { iPTR i32 } * { i32 i64 i128 } -> { iPTR i32 } - // Compute the difference between the two sets in such a way that the - // iPTR is in the set that is being subtracted. This is to see if there - // are any extra scalars in the set without iPTR that are not in the - // set containing iPTR. Then the iPTR could be considered a "wildcard" - // matching these scalars. If there is only one such scalar, it would - // replace the iPTR, if there are more, the iPTR would be retained. - SetType Diff; + // Let In' = elements only in In, Out' = elements only in Out, and + // IO = elements common to both. Normally IO would be returned as the result + // of the intersection, but we need to account for iPTR being a "wildcard" of + // sorts. Since elements in IO are those that match both sets exactly, they + // will all belong to the output. If any of the "leftovers" (i.e. In' or + // Out') contain iPTR, it means that the other set doesn't have it, but it + // could have (1) a more specific type, or (2) a set of types that is less + // specific. The "leftovers" from the other set is what we want to examine + // more closely. + + auto subtract = [](const SetType &A, const SetType &B) { + SetType Diff = A; + berase_if(Diff, [&B](MVT T) { return B.count(T); }); + return Diff; + }; + if (InP) { - Diff = Out; - berase_if(Diff, [&In](MVT T) { return In.count(T); }); - // Pre-remove these elements and rely only on InP/OutP to determine - // whether a change has been made. - berase_if(Out, [&Diff](MVT T) { return Diff.count(T); }); - } else { - Diff = In; - berase_if(Diff, [&Out](MVT T) { return Out.count(T); }); - Out.erase(MVT::iPTR); + SetType OutOnly = subtract(Out, In); + if (OutOnly.empty()) { + // This means that Out \subset In, so no change to Out. + return false; + } + unsigned NumI = llvm::count_if(OutOnly, isScalarInteger); + if (NumI == 1 && OutOnly.size() == 1) { + // There is only one element in Out', and it happens to be a scalar + // integer that should be kept as a match for iPTR in In. + return false; + } + berase_if(Out, CompIn); + if (NumI == 1) { + // Replace the iPTR with the leftover scalar integer. + Out.insert(*llvm::find_if(OutOnly, isScalarInteger)); + } else if (NumI > 1) { + Out.insert(MVT::iPTR); + } + return true; } - // The actual intersection. - bool Changed = berase_if(Out, Int); - unsigned NumD = Diff.size(); - if (NumD == 0) - return Changed; - - if (NumD == 1) { - Out.insert(*Diff.begin()); - // This is a change only if Out was the one with iPTR (which is now - // being replaced). - Changed |= OutP; - } else { - // Multiple elements from Out are now replaced with iPTR. - Out.insert(MVT::iPTR); - Changed |= !OutP; + // OutP == true + SetType InOnly = subtract(In, Out); + unsigned SizeOut = Out.size(); + berase_if(Out, CompIn); // This will remove at least the iPTR. + unsigned NumI = llvm::count_if(InOnly, isScalarInteger); + if (NumI == 0) { + // iPTR deleted from Out. + return true; } - return Changed; + if (NumI == 1) { + // Replace the iPTR with the leftover scalar integer. + Out.insert(*llvm::find_if(InOnly, isScalarInteger)); + return true; + } + + // NumI > 1: Keep the iPTR in Out. + Out.insert(MVT::iPTR); + // If iPTR was the only element initially removed from Out, then Out + // has not changed. + return SizeOut != Out.size(); } bool TypeSetByHwMode::validate() const { @@ -902,7 +932,7 @@ TreePredicateFn::TreePredicateFn(TreePattern *N) : PatFragRec(N) { } bool TreePredicateFn::hasPredCode() const { - return isLoad() || isStore() || isAtomic() || + return isLoad() || isStore() || isAtomic() || hasNoUse() || !PatFragRec->getRecord()->getValueAsString("PredicateCode").empty(); } @@ -947,12 +977,15 @@ std::string TreePredicateFn::getPredCode() const { if (isAnyExtLoad()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsAnyExtLoad requires IsLoad"); - if (isSignExtLoad()) - PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), - "IsSignExtLoad requires IsLoad"); - if (isZeroExtLoad()) - PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), - "IsZeroExtLoad requires IsLoad"); + + if (!isAtomic()) { + if (isSignExtLoad()) + PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), + "IsSignExtLoad requires IsLoad or IsAtomic"); + if (isZeroExtLoad()) + PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), + "IsZeroExtLoad requires IsLoad or IsAtomic"); + } } if (isStore()) { @@ -973,8 +1006,9 @@ std::string TreePredicateFn::getPredCode() const { if (isAtomic()) { if (getMemoryVT() == nullptr && !isAtomicOrderingMonotonic() && getAddressSpaces() == nullptr && - !isAtomicOrderingAcquire() && !isAtomicOrderingRelease() && - !isAtomicOrderingAcquireRelease() && + // FIXME: Should atomic loads be IsLoad, IsAtomic, or both? + !isZeroExtLoad() && !isSignExtLoad() && !isAtomicOrderingAcquire() && + !isAtomicOrderingRelease() && !isAtomicOrderingAcquireRelease() && !isAtomicOrderingSequentiallyConsistent() && !isAtomicOrderingAcquireOrStronger() && !isAtomicOrderingReleaseOrStronger() && @@ -1075,6 +1109,10 @@ std::string TreePredicateFn::getPredCode() const { Code += "if (isReleaseOrStronger(cast<AtomicSDNode>(N)->getMergedOrdering())) " "return false;\n"; + // TODO: Handle atomic sextload/zextload normally when ATOMIC_LOAD is removed. + if (isAtomic() && (isZeroExtLoad() || isSignExtLoad())) + Code += "return false;\n"; + if (isLoad() || isStore()) { StringRef SDNodeName = isLoad() ? "LoadSDNode" : "StoreSDNode"; @@ -1124,6 +1162,9 @@ std::string TreePredicateFn::getPredCode() const { .str(); } + if (hasNoUse()) + Code += "if (!SDValue(N, 0).use_empty()) return false;\n"; + std::string PredicateCode = std::string(PatFragRec->getRecord()->getValueAsString("PredicateCode")); @@ -1167,6 +1208,9 @@ bool TreePredicateFn::isPredefinedPredicateEqualTo(StringRef Field, bool TreePredicateFn::usesOperands() const { return isPredefinedPredicateEqualTo("PredicateCodeUsesOperands", true); } +bool TreePredicateFn::hasNoUse() const { + return isPredefinedPredicateEqualTo("HasNoUse", true); +} bool TreePredicateFn::isLoad() const { return isPredefinedPredicateEqualTo("IsLoad", true); } diff --git a/llvm/utils/TableGen/CodeGenDAGPatterns.h b/llvm/utils/TableGen/CodeGenDAGPatterns.h index 94694a96eb90..dbdc72f0873a 100644 --- a/llvm/utils/TableGen/CodeGenDAGPatterns.h +++ b/llvm/utils/TableGen/CodeGenDAGPatterns.h @@ -102,6 +102,8 @@ struct MachineValueTypeSet { Words[T.SimpleTy / WordWidth] &= ~(WordType(1) << (T.SimpleTy % WordWidth)); } + void writeToStream(raw_ostream &OS) const; + struct const_iterator { // Some implementations of the C++ library require these traits to be // defined. @@ -185,6 +187,8 @@ private: std::array<WordType,NumWords> Words; }; +raw_ostream &operator<<(raw_ostream &OS, const MachineValueTypeSet &T); + struct TypeSetByHwMode : public InfoByHwMode<MachineValueTypeSet> { using SetType = MachineValueTypeSet; SmallVector<unsigned, 16> AddrSpaces; @@ -239,7 +243,6 @@ struct TypeSetByHwMode : public InfoByHwMode<MachineValueTypeSet> { bool assign_if(const TypeSetByHwMode &VTS, Predicate P); void writeToStream(raw_ostream &OS) const; - static void writeToStream(const SetType &S, raw_ostream &OS); bool operator==(const TypeSetByHwMode &VTS) const; bool operator!=(const TypeSetByHwMode &VTS) const { return !(*this == VTS); } @@ -538,6 +541,9 @@ public: // Predicate code uses the PatFrag's captured operands. bool usesOperands() const; + // Check if the HasNoUse predicate is set. + bool hasNoUse() const; + // Is the desired predefined predicate for a load? bool isLoad() const; // Is the desired predefined predicate for a store? diff --git a/llvm/utils/TableGen/CodeGenRegisters.cpp b/llvm/utils/TableGen/CodeGenRegisters.cpp index 2c61be713afc..93ed86cfb7e5 100644 --- a/llvm/utils/TableGen/CodeGenRegisters.cpp +++ b/llvm/utils/TableGen/CodeGenRegisters.cpp @@ -861,6 +861,26 @@ void CodeGenRegisterClass::inheritProperties(CodeGenRegBank &RegBank) { Orders[i].push_back(Super.Orders[i][j]); } +bool CodeGenRegisterClass::hasType(const ValueTypeByHwMode &VT) const { + if (llvm::is_contained(VTs, VT)) + return true; + + // If VT is not identical to any of this class's types, but is a simple + // type, check if any of the types for this class contain it under some + // mode. + // The motivating example came from RISCV, where (likely because of being + // guarded by "64-bit" predicate), the type of X5 was {*:[i64]}, but the + // type in GRC was {*:[i32], m1:[i64]}. + if (VT.isSimple()) { + MVT T = VT.getSimple(); + for (const ValueTypeByHwMode &OurVT : VTs) { + if (llvm::count_if(OurVT, [T](auto &&P) { return P.second == T; })) + return true; + } + } + return false; +} + bool CodeGenRegisterClass::contains(const CodeGenRegister *Reg) const { return std::binary_search(Members.begin(), Members.end(), Reg, deref<std::less<>>()); diff --git a/llvm/utils/TableGen/CodeGenRegisters.h b/llvm/utils/TableGen/CodeGenRegisters.h index 0fc8b3ef80dd..e5e92fc81f50 100644 --- a/llvm/utils/TableGen/CodeGenRegisters.h +++ b/llvm/utils/TableGen/CodeGenRegisters.h @@ -351,10 +351,7 @@ namespace llvm { std::string getQualifiedName() const; ArrayRef<ValueTypeByHwMode> getValueTypes() const { return VTs; } unsigned getNumValueTypes() const { return VTs.size(); } - - bool hasType(const ValueTypeByHwMode &VT) const { - return llvm::is_contained(VTs, VT); - } + bool hasType(const ValueTypeByHwMode &VT) const; const ValueTypeByHwMode &getValueTypeNum(unsigned VTNum) const { if (VTNum < VTs.size()) diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp index c8eac56d03e6..4b47cda41567 100644 --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -331,6 +331,9 @@ static Error isTrivialOperatorNode(const TreePatternNode *N) { if (Predicate.isImmediatePattern()) continue; + if (Predicate.hasNoUse()) + continue; + if (Predicate.isNonExtLoad() || Predicate.isAnyExtLoad() || Predicate.isSignExtLoad() || Predicate.isZeroExtLoad()) continue; @@ -1119,6 +1122,7 @@ public: IPM_MemoryAddressSpace, IPM_MemoryAlignment, IPM_VectorSplatImm, + IPM_NoUse, IPM_GenericPredicate, OPM_SameOperand, OPM_ComplexPattern, @@ -2238,6 +2242,29 @@ public: } }; +/// 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::IntValue(InsnVarID) + << MatchTable::LineBreak; + } +}; + /// Generates code to check that a set of predicates and operands match for a /// particular instruction. /// @@ -2943,7 +2970,7 @@ public: << MatchTable::IntValue(RendererID); if (SubOperand) Table << MatchTable::Comment("SubOperand") - << MatchTable::IntValue(SubOperand.getValue()); + << MatchTable::IntValue(SubOperand.value()); Table << MatchTable::Comment(SymbolicName) << MatchTable::LineBreak; } }; @@ -3758,10 +3785,12 @@ GlobalISelEmitter::getEquivNode(Record &Equiv, const TreePatternNode *N) const { for (const TreePredicateCall &Call : N->getPredicateCalls()) { const TreePredicateFn &Predicate = Call.Fn; - if (!Equiv.isValueUnset("IfSignExtend") && Predicate.isLoad() && + if (!Equiv.isValueUnset("IfSignExtend") && + (Predicate.isLoad() || Predicate.isAtomic()) && Predicate.isSignExtLoad()) return &Target.getInstruction(Equiv.getValueAsDef("IfSignExtend")); - if (!Equiv.isValueUnset("IfZeroExtend") && Predicate.isLoad() && + if (!Equiv.isValueUnset("IfZeroExtend") && + (Predicate.isLoad() || Predicate.isAtomic()) && Predicate.isZeroExtLoad()) return &Target.getInstruction(Equiv.getValueAsDef("IfZeroExtend")); } @@ -4000,6 +4029,17 @@ Expected<InstructionMatcher &> GlobalISelEmitter::createAndImportSelDAGMatcher( 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<NoUsePredicateMatcher>(); + HasAddedBuiltinMatcher = true; + } + if (Predicate.hasGISelPredicateCode()) { if (Predicate.usesOperands()) { assert(WaitingForNamedOperands == 0 && @@ -4946,8 +4986,8 @@ Error GlobalISelEmitter::importDefaultOperandRenderers( auto Def = DefaultDefOp->getDef(); if (Def->getName() == "undef_tied_input") { unsigned TempRegID = M.allocateTempRegID(); - M.insertAction<MakeTempRegisterAction>( - InsertPt, OpTyOrNone.getValue(), TempRegID); + M.insertAction<MakeTempRegisterAction>(InsertPt, OpTyOrNone.value(), + TempRegID); InsertPt = M.insertAction<BuildMIAction>( InsertPt, M.allocateOutputInsnID(), &Target.getInstruction(RK.getDef("IMPLICIT_DEF"))); @@ -5206,16 +5246,31 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { auto &DstI = Target.getInstruction(DstOp); StringRef DstIName = DstI.TheDef->getName(); - if (DstI.Operands.NumDefs < Src->getExtTypes().size()) - return failedImport("Src pattern result has more defs than dst MI (" + - to_string(Src->getExtTypes().size()) + " def(s) vs " + - to_string(DstI.Operands.NumDefs) + " def(s))"); + 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<NoUsePredicateMatcher>(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; - for (const TypeSetByHwMode &VTy : Src->getExtTypes()) { - (void)VTy; + 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]; Record *DstIOpRec = DstIOperand.Rec; diff --git a/llvm/utils/TableGen/InstrInfoEmitter.cpp b/llvm/utils/TableGen/InstrInfoEmitter.cpp index a7a4f4f5f1a7..da8d0a0096fd 100644 --- a/llvm/utils/TableGen/InstrInfoEmitter.cpp +++ b/llvm/utils/TableGen/InstrInfoEmitter.cpp @@ -17,7 +17,9 @@ #include "CodeGenTarget.h" #include "PredicateExpander.h" #include "SequenceToOffsetTable.h" +#include "SubtargetFeatureInfo.h" #include "TableGenBackends.h" +#include "Types.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" @@ -79,6 +81,9 @@ private: /// Expand TIIPredicate definitions to functions that accept a const MCInst /// reference. void emitMCIIHelperMethods(raw_ostream &OS, StringRef TargetName); + + /// Write verifyInstructionPredicates methods. + void emitFeatureVerifier(raw_ostream &OS, const CodeGenTarget &Target); void emitRecord(const CodeGenInstruction &Inst, unsigned Num, Record *InstrInfo, std::map<std::vector<Record*>, unsigned> &EL, @@ -666,14 +671,13 @@ void InstrInfoEmitter::emitLogicalOperandTypeMappings( void InstrInfoEmitter::emitMCIIHelperMethods(raw_ostream &OS, StringRef TargetName) { RecVec TIIPredicates = Records.getAllDerivedDefinitions("TIIPredicate"); - if (TIIPredicates.empty()) - return; OS << "#ifdef GET_INSTRINFO_MC_HELPER_DECLS\n"; OS << "#undef GET_INSTRINFO_MC_HELPER_DECLS\n\n"; OS << "namespace llvm {\n"; - OS << "class MCInst;\n\n"; + OS << "class MCInst;\n"; + OS << "class FeatureBitset;\n\n"; OS << "namespace " << TargetName << "_MC {\n\n"; @@ -682,6 +686,9 @@ void InstrInfoEmitter::emitMCIIHelperMethods(raw_ostream &OS, << "(const MCInst &MI);\n"; } + OS << "void verifyInstructionPredicates(unsigned Opcode, const FeatureBitset " + "&Features);\n"; + OS << "\n} // end namespace " << TargetName << "_MC\n"; OS << "} // end namespace llvm\n\n"; @@ -708,7 +715,143 @@ void InstrInfoEmitter::emitMCIIHelperMethods(raw_ostream &OS, OS << "} // end namespace " << TargetName << "_MC\n"; OS << "} // end namespace llvm\n\n"; - OS << "#endif // GET_GENISTRINFO_MC_HELPERS\n"; + OS << "#endif // GET_GENISTRINFO_MC_HELPERS\n\n"; +} + +static std::string +getNameForFeatureBitset(const std::vector<Record *> &FeatureBitset) { + std::string Name = "CEFBS"; + for (const auto &Feature : FeatureBitset) + Name += ("_" + Feature->getName()).str(); + return Name; +} + +void InstrInfoEmitter::emitFeatureVerifier(raw_ostream &OS, + const CodeGenTarget &Target) { + const auto &All = SubtargetFeatureInfo::getAll(Records); + std::map<Record *, SubtargetFeatureInfo, LessRecordByID> SubtargetFeatures; + SubtargetFeatures.insert(All.begin(), All.end()); + + OS << "#ifdef ENABLE_INSTR_PREDICATE_VERIFIER\n" + << "#undef ENABLE_INSTR_PREDICATE_VERIFIER\n" + << "#include <sstream>\n\n"; + + OS << "namespace llvm {\n"; + OS << "namespace " << Target.getName() << "_MC {\n\n"; + + // Emit the subtarget feature enumeration. + SubtargetFeatureInfo::emitSubtargetFeatureBitEnumeration(SubtargetFeatures, + OS); + + // Emit the name table for error messages. + OS << "#ifndef NDEBUG\n"; + SubtargetFeatureInfo::emitNameTable(SubtargetFeatures, OS); + OS << "#endif // NDEBUG\n\n"; + + // Emit the available features compute function. + SubtargetFeatureInfo::emitComputeAssemblerAvailableFeatures( + Target.getName(), "", "computeAvailableFeatures", SubtargetFeatures, OS); + + std::vector<std::vector<Record *>> FeatureBitsets; + for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) { + FeatureBitsets.emplace_back(); + for (Record *Predicate : Inst->TheDef->getValueAsListOfDefs("Predicates")) { + const auto &I = SubtargetFeatures.find(Predicate); + if (I != SubtargetFeatures.end()) + FeatureBitsets.back().push_back(I->second.TheDef); + } + } + + llvm::sort(FeatureBitsets, [&](const std::vector<Record *> &A, + const std::vector<Record *> &B) { + if (A.size() < B.size()) + return true; + if (A.size() > B.size()) + return false; + for (auto Pair : zip(A, B)) { + if (std::get<0>(Pair)->getName() < std::get<1>(Pair)->getName()) + return true; + if (std::get<0>(Pair)->getName() > std::get<1>(Pair)->getName()) + return false; + } + return false; + }); + FeatureBitsets.erase( + std::unique(FeatureBitsets.begin(), FeatureBitsets.end()), + FeatureBitsets.end()); + OS << "#ifndef NDEBUG\n" + << "// Feature bitsets.\n" + << "enum : " << getMinimalTypeForRange(FeatureBitsets.size()) << " {\n" + << " CEFBS_None,\n"; + for (const auto &FeatureBitset : FeatureBitsets) { + if (FeatureBitset.empty()) + continue; + OS << " " << getNameForFeatureBitset(FeatureBitset) << ",\n"; + } + OS << "};\n\n" + << "static constexpr FeatureBitset FeatureBitsets[] = {\n" + << " {}, // CEFBS_None\n"; + for (const auto &FeatureBitset : FeatureBitsets) { + if (FeatureBitset.empty()) + continue; + OS << " {"; + for (const auto &Feature : FeatureBitset) { + const auto &I = SubtargetFeatures.find(Feature); + assert(I != SubtargetFeatures.end() && "Didn't import predicate?"); + OS << I->second.getEnumBitName() << ", "; + } + OS << "},\n"; + } + OS << "};\n" + << "#endif // NDEBUG\n\n"; + + // Emit the predicate verifier. + OS << "void verifyInstructionPredicates(\n" + << " unsigned Opcode, const FeatureBitset &Features) {\n" + << "#ifndef NDEBUG\n" + << " static " << getMinimalTypeForRange(FeatureBitsets.size()) + << " RequiredFeaturesRefs[] = {\n"; + unsigned InstIdx = 0; + for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) { + OS << " CEFBS"; + unsigned NumPredicates = 0; + for (Record *Predicate : Inst->TheDef->getValueAsListOfDefs("Predicates")) { + const auto &I = SubtargetFeatures.find(Predicate); + if (I != SubtargetFeatures.end()) { + OS << '_' << I->second.TheDef->getName(); + NumPredicates++; + } + } + if (!NumPredicates) + OS << "_None"; + OS << ", // " << Inst->TheDef->getName() << " = " << InstIdx << "\n"; + InstIdx++; + } + OS << " };\n\n"; + OS << " assert(Opcode < " << InstIdx << ");\n"; + OS << " FeatureBitset AvailableFeatures = " + "computeAvailableFeatures(Features);\n"; + OS << " const FeatureBitset &RequiredFeatures = " + "FeatureBitsets[RequiredFeaturesRefs[Opcode]];\n"; + OS << " FeatureBitset MissingFeatures =\n" + << " (AvailableFeatures & RequiredFeatures) ^\n" + << " RequiredFeatures;\n" + << " if (MissingFeatures.any()) {\n" + << " std::ostringstream Msg;\n" + << " Msg << \"Attempting to emit \" << &" << Target.getName() + << "InstrNameData[" << Target.getName() << "InstrNameIndices[Opcode]]\n" + << " << \" instruction but the \";\n" + << " for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i)\n" + << " if (MissingFeatures.test(i))\n" + << " Msg << SubtargetFeatureNames[i] << \" \";\n" + << " Msg << \"predicate(s) are not met\";\n" + << " report_fatal_error(Msg.str().c_str());\n" + << " }\n" + << "#endif // NDEBUG\n"; + OS << "}\n"; + OS << "} // end namespace " << Target.getName() << "_MC\n"; + OS << "} // end namespace llvm\n"; + OS << "#endif // ENABLE_INSTR_PREDICATE_VERIFIER\n\n"; } void InstrInfoEmitter::emitTIIHelperMethods(raw_ostream &OS, @@ -955,6 +1098,9 @@ void InstrInfoEmitter::run(raw_ostream &OS) { Records.startTimer("Emit helper methods"); emitMCIIHelperMethods(OS, TargetName); + + Records.startTimer("Emit verifier methods"); + emitFeatureVerifier(OS, Target); } void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num, diff --git a/llvm/utils/TableGen/SubtargetFeatureInfo.cpp b/llvm/utils/TableGen/SubtargetFeatureInfo.cpp index f4f360fb5be2..2a63fc490380 100644 --- a/llvm/utils/TableGen/SubtargetFeatureInfo.cpp +++ b/llvm/utils/TableGen/SubtargetFeatureInfo.cpp @@ -144,8 +144,13 @@ static bool emitFeaturesAux(StringRef TargetName, const Init &Val, void SubtargetFeatureInfo::emitComputeAssemblerAvailableFeatures( StringRef TargetName, StringRef ClassName, StringRef FuncName, SubtargetFeatureInfoMap &SubtargetFeatures, raw_ostream &OS) { - OS << "FeatureBitset " << TargetName << ClassName << "::\n" - << FuncName << "(const FeatureBitset &FB) const {\n"; + OS << "FeatureBitset "; + if (!ClassName.empty()) + OS << TargetName << ClassName << "::\n"; + OS << FuncName << "(const FeatureBitset &FB) "; + if (!ClassName.empty()) + OS << "const "; + OS << "{\n"; OS << " FeatureBitset Features;\n"; for (const auto &SF : SubtargetFeatures) { const SubtargetFeatureInfo &SFI = SF.second; |
