| //===- RuntimeLibcallEmitter.cpp - Properties from RuntimeLibcalls.td -----===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #define DEBUG_TYPE "runtime-libcall-emitter" | |
| #include "RuntimeLibcalls.h" | |
| #include "SequenceToOffsetTable.h" | |
| #include "llvm/ADT/DenseSet.h" | |
| #include "llvm/ADT/MapVector.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/ADT/StringRef.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/Format.h" | |
| #include "llvm/Support/FormatVariadic.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/Support/xxhash.h" | |
| #include "llvm/TableGen/CodeGenHelpers.h" | |
| #include "llvm/TableGen/Error.h" | |
| #include "llvm/TableGen/Record.h" | |
| #include "llvm/TableGen/SetTheory.h" | |
| #include "llvm/TableGen/StringToOffsetTable.h" | |
| #include "llvm/TableGen/TableGenBackend.h" | |
| using namespace llvm; | |
| namespace { | |
| // Pair of a RuntimeLibcallAvailability and LibcallCallingConv to use as a map | |
| // key. | |
| struct PredicateWithCC { | |
| const Record *Availability = nullptr; | |
| const Record *CallingConv = nullptr; | |
| PredicateWithCC() = default; | |
| PredicateWithCC(std::pair<const Record *, const Record *> P) | |
| : Availability(P.first), CallingConv(P.second) {} | |
| PredicateWithCC(const Record *P, const Record *C) | |
| : Availability(P), CallingConv(C) {} | |
| }; | |
| inline bool operator==(PredicateWithCC LHS, PredicateWithCC RHS) { | |
| return LHS.Availability == RHS.Availability && | |
| LHS.CallingConv == RHS.CallingConv; | |
| } | |
| } // namespace | |
| namespace { | |
| /// A floating-point libcall family parsed from a RuntimeLibcallFamily record. | |
| struct FPLibcallFamily { | |
| StringRef Base; | |
| std::vector<StringRef> Intrinsics; | |
| std::vector<StringRef> VectorSuffixes; | |
| explicit FPLibcallFamily(const Record *R) | |
| : Base(R->getValueAsString("LibcallBase")), | |
| Intrinsics(R->getValueAsListOfStrings("Intrinsics")), | |
| VectorSuffixes(R->getValueAsListOfStrings("VectorSuffixes")) {} | |
| }; | |
| } // namespace | |
| namespace llvm { | |
| template <> struct DenseMapInfo<PredicateWithCC, void> { | |
| static unsigned getHashValue(const PredicateWithCC Val) { | |
| auto Pair = std::make_pair(Val.Availability, Val.CallingConv); | |
| return DenseMapInfo< | |
| std::pair<const Record *, const Record *>>::getHashValue(Pair); | |
| } | |
| static bool isEqual(PredicateWithCC LHS, PredicateWithCC RHS) { | |
| return LHS == RHS; | |
| } | |
| }; | |
| class RuntimeLibcallEmitter { | |
| private: | |
| const RecordKeeper &Records; | |
| RuntimeLibcalls Libcalls; | |
| void emitGetRuntimeLibcallEnum(raw_ostream &OS) const; | |
| void emitNameMatchHashTable(raw_ostream &OS, | |
| StringToOffsetTable &OffsetTable) const; | |
| void emitGetInitRuntimeLibcallNames(raw_ostream &OS) const; | |
| void emitGetInitRuntimeLibcallSignatures(raw_ostream &OS) const; | |
| void emitMaskedVectorLibcallMapping(raw_ostream &OS) const; | |
| // Emit the sorted per-predicate `setAvailable` tables/loops. The | |
| // always-available bucket emits at \p BaseIndent; each predicated bucket is | |
| // wrapped in `if (pred)`. All calls are emitted in member context (no | |
| // receiver prefix). | |
| void | |
| emitPredicateGroups(raw_ostream &OS, const Record *R, | |
| DenseMap<PredicateWithCC, LibcallsWithCC> &Pred2Funcs, | |
| SetVector<PredicateWithCC> &PredicateSorter, | |
| unsigned BaseIndent) const; | |
| // A LibraryRef opt-out: the impls a consumer drops from a shared library, | |
| // plus the consumer's triple predicate. | |
| struct LibraryExclusion { | |
| const Record *TriplePred; | |
| std::vector<const RuntimeLibcallImpl *> Impls; | |
| }; | |
| // A SystemRuntimeLibrary's default calling convention and the libraries it | |
| // dispatches, as (LibraryName, FuncSuffix) pairs. | |
| struct SystemLibraryDispatch { | |
| const Record *System; | |
| StringRef DefaultCC; | |
| SetVector<std::pair<StringRef, StringRef>> Libs; | |
| }; | |
| // A single LibcallLibrary variant, expanded into its per-predicate impl | |
| // groups. Unconditional impls are tracked separately for cross-variant | |
| // deduplication. A variant is Deferred when it re-adds an impl its own | |
| // consumer excludes; deferred variants are emitted after the LibraryRef | |
| // exclusions so the re-add wins over the opt-out. | |
| struct ExpandedLibrary { | |
| const Record *Lib; | |
| DenseMap<PredicateWithCC, LibcallsWithCC> Pred2Funcs; | |
| SetVector<PredicateWithCC> PredicateSorter; | |
| SetVector<const RuntimeLibcallImpl *> Unconditional; | |
| bool Deferred = false; | |
| }; | |
| // Emit one variant's guarded `setAvailable` block into the enclosing | |
| // `setAvailableLibFuncs_<name>` function. | |
| void emitLibraryVariant(raw_ostream &OS, ExpandedLibrary &EL) const; | |
| // Emit a `setAvailableLibFuncs_<name>` member function for all LibcallLibrary | |
| // defs sharing \p Name, each gated by its own availability predicate. \p | |
| // Exclusions are emitted as guarded setUnavailable calls at the end. | |
| void emitLibraryFunction(raw_ostream &OS, StringRef Name, | |
| ArrayRef<const Record *> Libs, | |
| ArrayRef<LibraryExclusion> Exclusions) const; | |
| // Group all LibcallLibrary defs by their shared LibraryName, preserving | |
| // definition order. Both the member-declaration fragment and the definitions | |
| // iterate this to stay in lockstep. | |
| MapVector<StringRef, std::vector<const Record *>> | |
| collectLibrariesByName() const; | |
| // Get the sorted, unique list of every defined LibraryName. | |
| std::vector<StringRef> collectLibraryNames() const; | |
| void emitRuntimeLibcallsInfoMemberDecls(raw_ostream &OS) const; | |
| std::vector<SystemLibraryDispatch> collectSystemLibraryDispatch() const; | |
| void emitSystemRuntimeLibrarySetCalls( | |
| raw_ostream &OS, ArrayRef<SystemLibraryDispatch> Systems) const; | |
| // Emit setAvailableLibraryLibcalls, which enables a library by name. | |
| void emitLibraryByNameSetCalls(raw_ostream &OS, | |
| ArrayRef<SystemLibraryDispatch> Systems) const; | |
| DenseSet<StringRef> collectLibcallNames() const; | |
| void checkFPLibcallFamilies(ArrayRef<FPLibcallFamily> Families, | |
| const DenseSet<StringRef> &LibcallNames) const; | |
| void emitFPLibcallSelectorDecls(raw_ostream &OS, | |
| ArrayRef<FPLibcallFamily> Families) const; | |
| void emitFPLibcallSelectors(raw_ostream &OS, | |
| ArrayRef<FPLibcallFamily> Families, | |
| const DenseSet<StringRef> &LibcallNames) const; | |
| void | |
| emitGetLibcallForIntrinsic(raw_ostream &OS, | |
| ArrayRef<FPLibcallFamily> Families, | |
| const DenseSet<StringRef> &LibcallNames) const; | |
| public: | |
| RuntimeLibcallEmitter(const RecordKeeper &R) : Records(R), Libcalls(R) {} | |
| void run(raw_ostream &OS); | |
| }; | |
| } // End anonymous namespace. | |
| // Emit the linker name \p Name as a C++ identifier suffix, replacing characters | |
| // invalid in an identifier (e.g. the '-' in "compiler-rt") with '_'. | |
| static void emitLibFuncSuffix(raw_ostream &OS, StringRef Name) { | |
| for (char C : Name) | |
| OS << (isAlnum(C) || C == '_' ? C : '_'); | |
| } | |
| static void emitRuntimeLibraryEnumerator(raw_ostream &OS, StringRef Name) { | |
| OS << "RTLIB::RuntimeLibrary::"; | |
| emitLibFuncSuffix(OS, Name); | |
| } | |
| void RuntimeLibcallEmitter::emitGetRuntimeLibcallEnum(raw_ostream &OS) const { | |
| IfDefEmitter IfDef(OS, "GET_RUNTIME_LIBCALL_ENUM"); | |
| OS << "namespace llvm {\n" | |
| "namespace RTLIB {\n" | |
| "enum Libcall : unsigned short {\n"; | |
| for (const RuntimeLibcall &LibCall : Libcalls.getRuntimeLibcallDefList()) { | |
| StringRef Name = LibCall.getName(); | |
| OS << " " << Name << " = " << LibCall.getEnumVal() << ",\n"; | |
| } | |
| OS << " UNKNOWN_LIBCALL = " << Libcalls.getRuntimeLibcallDefList().size() | |
| << "\n};\n\n" | |
| "enum LibcallImpl : unsigned short {\n" | |
| " Unsupported = 0,\n"; | |
| for (const RuntimeLibcallImpl &LibCall : | |
| Libcalls.getRuntimeLibcallImplDefList()) { | |
| OS << " impl_" << LibCall.getName() << " = " << LibCall.getEnumVal() | |
| << ", // " << LibCall.getLibcallFuncName() << '\n'; | |
| } | |
| OS << "};\n" | |
| << "constexpr size_t NumLibcallImpls = " | |
| << Libcalls.getRuntimeLibcallImplDefList().size() + 1 | |
| << ";\n\n" | |
| "enum FuncArgTypeID : char {\n" | |
| " NoFuncArgType = 0,\n"; | |
| for (const auto *R : Libcalls.getFuncArgTypeList()) { | |
| if (R->getName() == "NoneType") | |
| continue; | |
| OS << " " << R->getName() << ",\n"; | |
| } | |
| OS << "};\n\n" | |
| "enum class RuntimeLibrary : unsigned char {\n" | |
| " UnknownLibrary = 0,\n"; | |
| for (StringRef Name : collectLibraryNames()) { | |
| OS << " "; | |
| emitLibFuncSuffix(OS, Name); | |
| OS << ",\n"; | |
| } | |
| OS << "};\n" | |
| "} // End namespace RTLIB\n" | |
| "} // End namespace llvm\n"; | |
| } | |
| // StringMap uses xxh3_64bits, truncated to uint32_t. | |
| static uint64_t hash(StringRef Str) { | |
| return static_cast<uint32_t>(xxh3_64bits(Str)); | |
| } | |
| static void emitHashFunction(raw_ostream &OS) { | |
| OS << "static inline uint64_t hash(StringRef Str) {\n" | |
| " return static_cast<uint32_t>(xxh3_64bits(Str));\n" | |
| "}\n\n"; | |
| } | |
| /// Return the table size, maximum number of collisions for the set of hashes | |
| static std::pair<int, int> | |
| computePerfectHashParameters(ArrayRef<uint64_t> Hashes) { | |
| // Chosen based on experimentation with llvm/benchmarks/RuntimeLibcalls.cpp | |
| const int SizeOverhead = 4; | |
| // Index derived from hash -> number of collisions. | |
| DenseMap<uint64_t, int> Table; | |
| unsigned NumHashes = Hashes.size(); | |
| for (int MaxCollisions = 1;; ++MaxCollisions) { | |
| for (unsigned N = NextPowerOf2(NumHashes - 1); N < SizeOverhead * NumHashes; | |
| N <<= 1) { | |
| Table.clear(); | |
| bool NeedResize = false; | |
| for (uint64_t H : Hashes) { | |
| uint64_t Idx = H % static_cast<uint64_t>(N); | |
| if (++Table[Idx] > MaxCollisions) { | |
| // Need to resize the final table if we increased the collision count. | |
| NeedResize = true; | |
| break; | |
| } | |
| } | |
| if (!NeedResize) | |
| return {N, MaxCollisions}; | |
| } | |
| } | |
| } | |
| static std::vector<unsigned> | |
| constructPerfectHashTable(ArrayRef<RuntimeLibcallImpl> Keywords, | |
| ArrayRef<uint64_t> Hashes, | |
| ArrayRef<unsigned> TableValues, int Size, | |
| int Collisions, StringToOffsetTable &OffsetTable) { | |
| std::vector<unsigned> Lookup(Size * Collisions); | |
| for (auto [HashValue, TableValue] : zip(Hashes, TableValues)) { | |
| uint64_t Idx = (HashValue % static_cast<uint64_t>(Size)) * | |
| static_cast<uint64_t>(Collisions); | |
| bool Found = false; | |
| for (int J = 0; J < Collisions; ++J) { | |
| unsigned &Entry = Lookup[Idx + J]; | |
| if (Entry == 0) { | |
| Entry = TableValue; | |
| Found = true; | |
| break; | |
| } | |
| } | |
| if (!Found) | |
| reportFatalInternalError("failure to hash"); | |
| } | |
| return Lookup; | |
| } | |
| /// Generate hash table based lookup by name. | |
| void RuntimeLibcallEmitter::emitNameMatchHashTable( | |
| raw_ostream &OS, StringToOffsetTable &OffsetTable) const { | |
| ArrayRef<RuntimeLibcallImpl> RuntimeLibcallImplDefList = | |
| Libcalls.getRuntimeLibcallImplDefList(); | |
| std::vector<uint64_t> Hashes(RuntimeLibcallImplDefList.size()); | |
| std::vector<unsigned> TableValues(RuntimeLibcallImplDefList.size()); | |
| DenseSet<StringRef> SeenFuncNames; | |
| size_t MaxFuncNameSize = 0; | |
| size_t Index = 0; | |
| for (const RuntimeLibcallImpl &LibCallImpl : RuntimeLibcallImplDefList) { | |
| StringRef ImplName = LibCallImpl.getLibcallFuncName(); | |
| if (SeenFuncNames.insert(ImplName).second) { | |
| MaxFuncNameSize = std::max(MaxFuncNameSize, ImplName.size()); | |
| TableValues[Index] = LibCallImpl.getEnumVal(); | |
| Hashes[Index++] = hash(ImplName); | |
| } | |
| } | |
| // Trim excess elements from non-unique entries. | |
| Hashes.resize(SeenFuncNames.size()); | |
| TableValues.resize(SeenFuncNames.size()); | |
| LLVM_DEBUG({ | |
| for (const RuntimeLibcallImpl &LibCallImpl : RuntimeLibcallImplDefList) { | |
| StringRef ImplName = LibCallImpl.getLibcallFuncName(); | |
| if (ImplName.size() == MaxFuncNameSize) { | |
| dbgs() << "Maximum runtime libcall name size: " << ImplName << '(' | |
| << MaxFuncNameSize << ")\n"; | |
| } | |
| } | |
| }); | |
| // Early exiting on the symbol name provides a significant speedup in the miss | |
| // case on the set of symbols in a clang binary. Emit this as an inlinable | |
| // precondition in the header. | |
| // | |
| // The empty check is also used to get sensible behavior on anonymous | |
| // functions. | |
| // | |
| // TODO: It may make more sense to split the search by string size more. There | |
| // are a few outliers, most call names are small. | |
| { | |
| IfDefEmitter IfDef(OS, "GET_LOOKUP_LIBCALL_IMPL_NAME_BODY"); | |
| OS << " size_t Size = Name.size();\n" | |
| " if (Size == 0 || Size > " | |
| << MaxFuncNameSize | |
| << ")\n" | |
| " return enum_seq(RTLIB::Unsupported, RTLIB::Unsupported);\n" | |
| " return lookupLibcallImplNameImpl(Name);\n"; | |
| } | |
| auto [Size, Collisions] = computePerfectHashParameters(Hashes); | |
| std::vector<unsigned> Lookup = | |
| constructPerfectHashTable(RuntimeLibcallImplDefList, Hashes, TableValues, | |
| Size, Collisions, OffsetTable); | |
| LLVM_DEBUG(dbgs() << "Runtime libcall perfect hashing parameters: Size = " | |
| << Size << ", maximum collisions = " << Collisions << '\n'); | |
| IfDefEmitter IfDef(OS, "DEFINE_GET_LOOKUP_LIBCALL_IMPL_NAME"); | |
| emitHashFunction(OS); | |
| OS << "iota_range<RTLIB::LibcallImpl> RTLIB::RuntimeLibcallsInfo::" | |
| "lookupLibcallImplNameImpl(StringRef Name) {\n"; | |
| // Emit RTLIB::LibcallImpl values | |
| OS << " static constexpr uint16_t HashTableNameToEnum[" << Lookup.size() | |
| << "] = {\n"; | |
| for (unsigned TableVal : Lookup) | |
| OS << " " << TableVal << ",\n"; | |
| OS << " };\n\n"; | |
| OS << " unsigned Idx = (hash(Name) % " << Size << ") * " << Collisions | |
| << ";\n\n" | |
| " for (int I = 0; I != " | |
| << Collisions << R"(; ++I) { | |
| const uint16_t Entry = HashTableNameToEnum[Idx + I]; | |
| const uint16_t StrOffset = RuntimeLibcallNameOffsetTable[Entry]; | |
| const uint8_t StrSize = RuntimeLibcallNameSizeTable[Entry]; | |
| StringRef Str( | |
| &RTLIB::RuntimeLibcallsInfo::RuntimeLibcallImplNameTableStorage[StrOffset], | |
| StrSize); | |
| if (Str == Name) | |
| return libcallImplNameHit(Entry, StrOffset); | |
| } | |
| return enum_seq(RTLIB::Unsupported, RTLIB::Unsupported); | |
| } | |
| )"; | |
| } | |
| void RuntimeLibcallEmitter::emitGetInitRuntimeLibcallNames( | |
| raw_ostream &OS) const { | |
| // Emit the implementation names | |
| StringToOffsetTable Table(/*AppendZero=*/true, | |
| "RTLIB::RuntimeLibcallsInfo::"); | |
| { | |
| IfDefEmitter IfDef(OS, "GET_INIT_RUNTIME_LIBCALL_NAMES"); | |
| for (const RuntimeLibcallImpl &LibCallImpl : | |
| Libcalls.getRuntimeLibcallImplDefList()) | |
| Table.GetOrAddStringOffset(LibCallImpl.getLibcallFuncName()); | |
| Table.EmitStringTableDef(OS, "RuntimeLibcallImplNameTable"); | |
| OS << R"( | |
| const uint16_t RTLIB::RuntimeLibcallsInfo::RuntimeLibcallNameOffsetTable[] = { | |
| )"; | |
| OS << formatv(" {}, // {}\n", Table.GetStringOffset(""), | |
| ""); // Unsupported entry | |
| for (const RuntimeLibcallImpl &LibCallImpl : | |
| Libcalls.getRuntimeLibcallImplDefList()) { | |
| StringRef ImplName = LibCallImpl.getLibcallFuncName(); | |
| OS << formatv(" {}, // {}\n", Table.GetStringOffset(ImplName), ImplName); | |
| } | |
| OS << "};\n"; | |
| OS << R"( | |
| const uint8_t RTLIB::RuntimeLibcallsInfo::RuntimeLibcallNameSizeTable[] = { | |
| )"; | |
| OS << " 0,\n"; | |
| for (const RuntimeLibcallImpl &LibCallImpl : | |
| Libcalls.getRuntimeLibcallImplDefList()) | |
| OS << " " << LibCallImpl.getLibcallFuncName().size() << ",\n"; | |
| OS << "};\n\n"; | |
| // Emit the reverse mapping from implementation libraries to RTLIB::Libcall | |
| OS << "const RTLIB::Libcall llvm::RTLIB::RuntimeLibcallsInfo::" | |
| "ImplToLibcall[RTLIB::NumLibcallImpls] = {\n" | |
| " RTLIB::UNKNOWN_LIBCALL, // RTLIB::Unsupported\n"; | |
| for (const RuntimeLibcallImpl &LibCallImpl : | |
| Libcalls.getRuntimeLibcallImplDefList()) { | |
| const RuntimeLibcall *Provides = LibCallImpl.getProvides(); | |
| OS << " "; | |
| Provides->emitEnumEntry(OS); | |
| OS << ", // "; | |
| LibCallImpl.emitEnumEntry(OS); | |
| OS << '\n'; | |
| } | |
| OS << "};\n\n"; | |
| } | |
| emitNameMatchHashTable(OS, Table); | |
| } | |
| using Signature = std::vector<StringRef>; | |
| static Signature getSignature(const Record *R) { | |
| const auto *Tys = R->getValueAsListInit("ArgumentTypes"); | |
| Signature Sig; | |
| Sig.reserve(Tys->size() + 1); | |
| const Record *RetType = R->getValueAsOptionalDef("ReturnType"); | |
| if (RetType && RetType->getName() != "NoneType") | |
| Sig.push_back(RetType->getName()); | |
| for (unsigned I = 0, E = Tys->size(); I < E; ++I) | |
| Sig.push_back(Tys->getElementAsRecord(I)->getName()); | |
| return Sig; | |
| } | |
| void RuntimeLibcallEmitter::emitGetInitRuntimeLibcallSignatures( | |
| raw_ostream &OS) const { | |
| SequenceToOffsetTable<Signature> SignatureTable("NoFuncArgType"); | |
| for (const RuntimeLibcall &LC : Libcalls.getRuntimeLibcallDefList()) | |
| SignatureTable.add(getSignature(LC.getDef())); | |
| SignatureTable.layout(); | |
| IfDefEmitter IfDef(OS, "GET_INIT_RUNTIME_LIBCALL_SIGNATURES"); | |
| OS << R"( | |
| const FuncArgTypeID RTLIB::RuntimeLibcallsInfo::SignatureTable[] = { | |
| )"; | |
| SignatureTable.emit(OS, [](raw_ostream &OS, StringRef E) { OS << E; }); | |
| OS << "};\n"; | |
| OS << R"( | |
| const uint16_t RTLIB::RuntimeLibcallsInfo::SignatureOffset[] = { | |
| )"; | |
| for (const RuntimeLibcall &LC : Libcalls.getRuntimeLibcallDefList()) { | |
| const Record *LibcallDef = LC.getDef(); | |
| OS << formatv(" {}, // {}\n", SignatureTable.get(getSignature(LibcallDef)), | |
| LibcallDef->getName()); | |
| } | |
| OS << "};\n"; | |
| } | |
| static void emitMaskedVectorLibcallSwitch( | |
| raw_ostream &OS, StringRef FuncName, bool ToUnmasked, | |
| ArrayRef<std::pair<const RuntimeLibcall *, const RuntimeLibcall *>> | |
| MaskedToUnmasked) { | |
| OS << "RTLIB::Libcall RTLIB::RuntimeLibcallsInfo::" << FuncName | |
| << "(RTLIB::Libcall LC) {\n" | |
| " switch (LC) {\n"; | |
| for (auto [Masked, Unmasked] : MaskedToUnmasked) { | |
| OS << " case "; | |
| if (ToUnmasked) { | |
| Masked->emitEnumEntry(OS); | |
| OS << ":\n return "; | |
| Unmasked->emitEnumEntry(OS); | |
| } else { | |
| Unmasked->emitEnumEntry(OS); | |
| OS << ":\n return "; | |
| Masked->emitEnumEntry(OS); | |
| } | |
| OS << ";\n"; | |
| } | |
| OS << " default:\n" | |
| " return RTLIB::UNKNOWN_LIBCALL;\n" | |
| " }\n" | |
| "}\n"; | |
| } | |
| void RuntimeLibcallEmitter::emitMaskedVectorLibcallMapping( | |
| raw_ostream &OS) const { | |
| IfDefEmitter IfDef(OS, "GET_RUNTIME_LIBCALL_MASKED_VECTOR_LIBCALL_MAPPING"); | |
| SmallVector<std::pair<const RuntimeLibcall *, const RuntimeLibcall *>, 0> | |
| MaskedToUnmasked; | |
| for (const RuntimeLibcall &LC : Libcalls.getRuntimeLibcallDefList()) { | |
| const Record *Def = LC.getDef(); | |
| if (Def->isSubClassOf("MaskedRuntimeLibcall")) { | |
| MaskedToUnmasked.emplace_back( | |
| &LC, Libcalls.getRuntimeLibcall(Def->getValueAsDef("Unmasked"))); | |
| } | |
| } | |
| emitMaskedVectorLibcallSwitch(OS, "getUnmaskedVectorLibcall", | |
| /*ToUnmasked=*/true, MaskedToUnmasked); | |
| OS << '\n'; | |
| emitMaskedVectorLibcallSwitch(OS, "getMaskedVectorLibcall", | |
| /*ToUnmasked=*/false, MaskedToUnmasked); | |
| } | |
| void RuntimeLibcallEmitter::emitPredicateGroups( | |
| raw_ostream &OS, const Record *R, | |
| DenseMap<PredicateWithCC, LibcallsWithCC> &Pred2Funcs, | |
| SetVector<PredicateWithCC> &PredicateSorter, unsigned BaseIndent) const { | |
| SmallVector<PredicateWithCC, 0> SortedPredicates = | |
| PredicateSorter.takeVector(); | |
| llvm::sort(SortedPredicates, [](PredicateWithCC A, PredicateWithCC B) { | |
| StringRef AName = A.Availability ? A.Availability->getName() : ""; | |
| StringRef BName = B.Availability ? B.Availability->getName() : ""; | |
| if (AName != BName) | |
| return AName < BName; | |
| // Break name ties on the calling convention for a deterministic order. | |
| StringRef ACC = A.CallingConv ? A.CallingConv->getName() : ""; | |
| StringRef BCC = B.CallingConv ? B.CallingConv->getName() : ""; | |
| return ACC < BCC; | |
| }); | |
| for (PredicateWithCC Entry : SortedPredicates) { | |
| AvailabilityPredicate SubsetPredicate(Entry.Availability); | |
| unsigned IndentDepth = BaseIndent; | |
| auto It = Pred2Funcs.find(Entry); | |
| if (It == Pred2Funcs.end()) | |
| continue; | |
| // Shared-core deduplication can empty a bucket. | |
| if (It->second.LibcallImpls.empty()) | |
| continue; | |
| if (!SubsetPredicate.isAlwaysAvailable()) { | |
| IndentDepth = BaseIndent + 2; | |
| OS << indent(IndentDepth); | |
| SubsetPredicate.emitIf(OS); | |
| } | |
| LibcallsWithCC &FuncsWithCC = It->second; | |
| std::vector<const RuntimeLibcallImpl *> &Funcs = FuncsWithCC.LibcallImpls; | |
| // Records which impls are available, not which is selected, so a libcall | |
| // may have more than one. Order is irrelevant (each entry is a setAvailable | |
| // call); sort by the provided libcall, breaking ties on the impl enum for a | |
| // deterministic total order. | |
| llvm::sort(Funcs, [](const RuntimeLibcallImpl *A, | |
| const RuntimeLibcallImpl *B) { | |
| return std::make_pair(A->getProvides()->getEnumVal(), A->getEnumVal()) < | |
| std::make_pair(B->getProvides()->getEnumVal(), B->getEnumVal()); | |
| }); | |
| OS << indent(IndentDepth + 2) | |
| << "static const RTLIB::LibcallImpl LibraryCalls"; | |
| SubsetPredicate.emitTableVariableNameSuffix(OS); | |
| if (FuncsWithCC.CallingConv) | |
| OS << '_' << FuncsWithCC.CallingConv->getName(); | |
| OS << "[] = {\n"; | |
| for (const RuntimeLibcallImpl *LibCallImpl : Funcs) { | |
| OS << indent(IndentDepth + 6); | |
| LibCallImpl->emitEnumEntry(OS); | |
| OS << ", // " << LibCallImpl->getLibcallFuncName() << '\n'; | |
| } | |
| OS << indent(IndentDepth + 2) << "};\n\n" | |
| << indent(IndentDepth + 2) | |
| << "for (const RTLIB::LibcallImpl Impl : LibraryCalls"; | |
| SubsetPredicate.emitTableVariableNameSuffix(OS); | |
| if (FuncsWithCC.CallingConv) | |
| OS << '_' << FuncsWithCC.CallingConv->getName(); | |
| OS << ") {\n" << indent(IndentDepth + 4) << "setAvailable(Impl);\n"; | |
| if (FuncsWithCC.CallingConv) { | |
| StringRef CCEnum = | |
| FuncsWithCC.CallingConv->getValueAsString("CallingConv"); | |
| OS << indent(IndentDepth + 4) << "setLibcallImplCallingConv(Impl, " | |
| << CCEnum << ");\n"; | |
| } | |
| OS << indent(IndentDepth + 2) << "}\n"; | |
| OS << '\n'; | |
| if (!SubsetPredicate.isAlwaysAvailable()) { | |
| OS << indent(IndentDepth); | |
| SubsetPredicate.emitEndIf(OS); | |
| OS << '\n'; | |
| } | |
| } | |
| } | |
| void RuntimeLibcallEmitter::emitLibraryVariant(raw_ostream &OS, | |
| ExpandedLibrary &EL) const { | |
| AvailabilityPredicate LibPred(EL.Lib->getValueAsDef("Pred")); | |
| if (!LibPred.isAlwaysAvailable()) { | |
| OS << indent(2); | |
| LibPred.emitIf(OS); | |
| } else { | |
| // Own block scope so per-variant `LibraryCalls` tables do not collide. | |
| OS << indent(2) << "{\n"; | |
| } | |
| emitPredicateGroups(OS, EL.Lib, EL.Pred2Funcs, EL.PredicateSorter, | |
| /*BaseIndent=*/2); | |
| if (!LibPred.isAlwaysAvailable()) { | |
| OS << indent(2); | |
| LibPred.emitEndIf(OS); | |
| } else { | |
| OS << indent(2) << "}\n"; | |
| } | |
| } | |
| void RuntimeLibcallEmitter::emitLibraryFunction( | |
| raw_ostream &OS, StringRef Name, ArrayRef<const Record *> Libs, | |
| ArrayRef<LibraryExclusion> Exclusions) const { | |
| OS << "void llvm::RTLIB::RuntimeLibcallsInfo::setAvailableLibFuncs_"; | |
| emitLibFuncSuffix(OS, Name); | |
| OS << "(const llvm::Triple &TT, " | |
| "ExceptionHandling ExceptionModel, FloatABI::ABIType FloatABI, " | |
| "StringRef ABIName, " | |
| "LongDoubleFormat LongDoubleFormat, CallingConv::ID DefaultCC) {\n"; | |
| SmallVector<ExpandedLibrary, 2> Expanded; | |
| for (const Record *Lib : Libs) { | |
| ExpandedLibrary EL; | |
| EL.Lib = Lib; | |
| // Expand this library's members with a library-local Func2Preds. | |
| SetTheory Sets; | |
| DenseMap<const RuntimeLibcallImpl *, | |
| std::pair<std::vector<const Record *>, const Record *>> | |
| Func2Preds; | |
| Sets.addExpander("LibcallImpls", std::make_unique<LibcallPredicateExpander>( | |
| Libcalls, Func2Preds)); | |
| SetTheory::RecSet Elements; | |
| Sets.evaluate(Lib->getValueInit("Impls"), Elements, Lib->getLoc()); | |
| EL.PredicateSorter.insert( | |
| PredicateWithCC()); // No predicate or CC override first. | |
| for (const Record *Elt : Elements) { | |
| const RuntimeLibcallImpl *LibCallImpl = | |
| Libcalls.getRuntimeLibcallImpl(Elt); | |
| if (!LibCallImpl) { | |
| PrintError(Lib, "entry for LibcallLibrary is not a RuntimeLibcallImpl"); | |
| PrintNote(Elt->getLoc(), "invalid entry `" + Elt->getName() + "`"); | |
| continue; | |
| } | |
| auto It = Func2Preds.find(LibCallImpl); | |
| if (It == Func2Preds.end()) { | |
| EL.Pred2Funcs[PredicateWithCC()].LibcallImpls.push_back(LibCallImpl); | |
| EL.Unconditional.insert(LibCallImpl); | |
| continue; | |
| } | |
| for (const Record *Pred : It->second.first) { | |
| const Record *CC = It->second.second; | |
| PredicateWithCC Key(Pred, CC); | |
| auto &Entry = EL.Pred2Funcs[Key]; | |
| Entry.LibcallImpls.push_back(LibCallImpl); | |
| Entry.CallingConv = CC; | |
| EL.PredicateSorter.insert(Key); | |
| } | |
| } | |
| Expanded.push_back(std::move(EL)); | |
| } | |
| // Impls unconditional in every variant are emitted once and stripped from | |
| // each variant, so the shared core is not repeated. | |
| SetVector<const RuntimeLibcallImpl *> SharedCore; | |
| if (Expanded.size() > 1) { | |
| for (const RuntimeLibcallImpl *Impl : Expanded.front().Unconditional) { | |
| if (all_of(drop_begin(Expanded), [&](const ExpandedLibrary &EL) { | |
| return EL.Unconditional.contains(Impl); | |
| })) | |
| SharedCore.insert(Impl); | |
| } | |
| } | |
| if (!SharedCore.empty()) { | |
| // Emit the shared core once, then strip it from every variant. | |
| DenseMap<PredicateWithCC, LibcallsWithCC> CorePred2Funcs; | |
| SetVector<PredicateWithCC> CoreSorter; | |
| CoreSorter.insert(PredicateWithCC()); | |
| for (const RuntimeLibcallImpl *Impl : SharedCore) | |
| CorePred2Funcs[PredicateWithCC()].LibcallImpls.push_back(Impl); | |
| emitPredicateGroups(OS, Libs.front(), CorePred2Funcs, CoreSorter, | |
| /*BaseIndent=*/0); | |
| for (ExpandedLibrary &EL : Expanded) { | |
| auto &Funcs = EL.Pred2Funcs[PredicateWithCC()].LibcallImpls; | |
| llvm::erase_if(Funcs, [&](const RuntimeLibcallImpl *Impl) { | |
| return SharedCore.contains(Impl); | |
| }); | |
| } | |
| } | |
| // Mark a variant deferred when it re-adds an impl its own consumer excludes | |
| // (same triple, via LibraryRef). Such a variant must be emitted after the | |
| // exclusion so the re-add wins while the exclusion still suppresses every | |
| // other variant's contribution. | |
| for (ExpandedLibrary &EL : Expanded) { | |
| const Record *ELPred = EL.Lib->getValueAsDef("Pred"); | |
| SetVector<const RuntimeLibcallImpl *> Impls; | |
| for (const auto &[Key, Funcs] : EL.Pred2Funcs) | |
| Impls.insert(Funcs.LibcallImpls.begin(), Funcs.LibcallImpls.end()); | |
| for (const LibraryExclusion &Excl : Exclusions) { | |
| if (Excl.TriplePred != ELPred) | |
| continue; | |
| if (any_of(Excl.Impls, [&](const RuntimeLibcallImpl *Impl) { | |
| return Impls.contains(Impl); | |
| })) { | |
| EL.Deferred = true; | |
| break; | |
| } | |
| } | |
| } | |
| // Emit each non-deferred variant under its own Pred. | |
| for (ExpandedLibrary &EL : Expanded) { | |
| if (!EL.Deferred) | |
| emitLibraryVariant(OS, EL); | |
| } | |
| // Emit each consumer's LibraryRef opt-outs. | |
| for (const LibraryExclusion &Excl : Exclusions) { | |
| OS << '\n' << indent(2); | |
| AvailabilityPredicate ExcludePred(Excl.TriplePred); | |
| ExcludePred.emitIf(OS); | |
| for (const RuntimeLibcallImpl *Impl : Excl.Impls) { | |
| OS << indent(4) << "setUnavailable("; | |
| Impl->emitEnumEntry(OS); | |
| OS << "); // " << Impl->getLibcallFuncName() << '\n'; | |
| } | |
| OS << indent(2); | |
| ExcludePred.emitEndIf(OS); | |
| } | |
| // Deferred variants: emitted after exclusions so a target's own re-adds | |
| // override its own LibraryRef opt-outs (the exclusion still applied above | |
| // to every other variant's contributions). | |
| for (ExpandedLibrary &EL : Expanded) { | |
| if (EL.Deferred) | |
| emitLibraryVariant(OS, EL); | |
| } | |
| OS << "}\n\n"; | |
| } | |
| // The setAvailableLibFuncs_ suffix (and merge key) for a library: its shared | |
| // LibraryName normally, or its own def name if isolated (so it does not merge). | |
| static StringRef libFuncKey(const Record *Lib) { | |
| return Lib->getValueAsBit("Isolated") ? Lib->getName() | |
| : Lib->getValueAsString("LibraryName"); | |
| } | |
| MapVector<StringRef, std::vector<const Record *>> | |
| RuntimeLibcallEmitter::collectLibrariesByName() const { | |
| MapVector<StringRef, std::vector<const Record *>> LibsByName; | |
| for (const Record *Lib : Records.getAllDerivedDefinitions("LibcallLibrary")) | |
| LibsByName[libFuncKey(Lib)].push_back(Lib); | |
| return LibsByName; | |
| } | |
| std::vector<StringRef> RuntimeLibcallEmitter::collectLibraryNames() const { | |
| std::vector<StringRef> Names; | |
| for (const Record *Lib : Records.getAllDerivedDefinitions("LibcallLibrary")) | |
| Names.push_back(Lib->getValueAsString("LibraryName")); | |
| llvm::sort(Names); | |
| Names.erase(llvm::unique(Names), Names.end()); | |
| return Names; | |
| } | |
| void RuntimeLibcallEmitter::emitRuntimeLibcallsInfoMemberDecls( | |
| raw_ostream &OS) const { | |
| IfDefEmitter IfDef(OS, "GET_RUNTIME_LIBCALLS_INFO_MEMBER_DECLS"); | |
| for (const auto &[Name, Libs] : collectLibrariesByName()) { | |
| OS << "void setAvailableLibFuncs_"; | |
| emitLibFuncSuffix(OS, Name); | |
| OS << "(const llvm::Triple &TT, ExceptionHandling ExceptionModel, " | |
| "FloatABI::ABIType FloatABI, StringRef ABIName, " | |
| "LongDoubleFormat LongDoubleFormat, CallingConv::ID DefaultCC);\n"; | |
| } | |
| OS << "bool setAvailableLibraryLibcalls(RTLIB::RuntimeLibrary Library, " | |
| "const llvm::Triple &TT, ExceptionHandling ExceptionModel, " | |
| "FloatABI::ABIType FloatABI, StringRef ABIName, " | |
| "LongDoubleFormat LongDoubleFormat);\n"; | |
| } | |
| std::vector<RuntimeLibcallEmitter::SystemLibraryDispatch> | |
| RuntimeLibcallEmitter::collectSystemLibraryDispatch() const { | |
| std::vector<SystemLibraryDispatch> Systems; | |
| for (const Record *R : | |
| Records.getAllDerivedDefinitions("SystemRuntimeLibrary")) { | |
| SystemLibraryDispatch &S = Systems.emplace_back(); | |
| S.System = R; | |
| if (const Record *DefaultCCClass = | |
| R->getValueAsDef("DefaultLibcallCallingConv")) | |
| S.DefaultCC = DefaultCCClass->getValueAsString("CallingConv").trim(); | |
| // A library referenced more than once is dispatched once. | |
| const DagInit *MemberDag = | |
| R->getValueAsDef("MemberList")->getValueAsDag("MemberList"); | |
| for (const Init *Arg : MemberDag->getArgs()) { | |
| const auto *DI = dyn_cast<DefInit>(Arg); | |
| const Record *Lib = DI ? DI->getDef() : nullptr; | |
| if (Lib && Lib->isSubClassOf("LibraryRef")) | |
| Lib = Lib->getValueAsDef("Library"); | |
| if (!Lib || !Lib->isSubClassOf("LibcallLibrary")) { | |
| PrintError(R, "SystemRuntimeLibrary member `" + Arg->getAsString() + | |
| "` is not a LibcallLibrary or LibraryRef"); | |
| continue; | |
| } | |
| S.Libs.insert({Lib->getValueAsString("LibraryName"), libFuncKey(Lib)}); | |
| } | |
| } | |
| return Systems; | |
| } | |
| void RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls( | |
| raw_ostream &OS, ArrayRef<SystemLibraryDispatch> Systems) const { | |
| // Collect each shared library's LibraryRef opt-outs, keyed by library name, | |
| // so its library function can emit them. | |
| MapVector<StringRef, std::vector<LibraryExclusion>> ExclusionsByLibName; | |
| for (const SystemLibraryDispatch &S : Systems) { | |
| const Record *R = S.System; | |
| const DagInit *MemberDag = | |
| R->getValueAsDef("MemberList")->getValueAsDag("MemberList"); | |
| for (const Init *Arg : MemberDag->getArgs()) { | |
| const auto *DI = dyn_cast<DefInit>(Arg); | |
| if (!DI || !DI->getDef()->isSubClassOf("LibraryRef")) | |
| continue; | |
| const Record *Def = DI->getDef(); | |
| LibraryExclusion Excl{R->getValueAsDef("TriplePred"), {}}; | |
| for (const Record *ExcludeRec : Def->getValueAsListOfDefs("Exclude")) { | |
| if (const RuntimeLibcallImpl *Impl = | |
| Libcalls.getRuntimeLibcallImpl(ExcludeRec)) | |
| Excl.Impls.push_back(Impl); | |
| } | |
| if (!Excl.Impls.empty()) { | |
| StringRef LibName = | |
| Def->getValueAsDef("Library")->getValueAsString("LibraryName"); | |
| ExclusionsByLibName[LibName].push_back(std::move(Excl)); | |
| } | |
| } | |
| } | |
| for (const auto &[Name, Libs] : collectLibrariesByName()) | |
| emitLibraryFunction(OS, Name, Libs, ExclusionsByLibName.lookup(Name)); | |
| OS << "void llvm::RTLIB::RuntimeLibcallsInfo::setTargetRuntimeLibcallSets(" | |
| "const llvm::Triple &TT, ExceptionHandling ExceptionModel, " | |
| "FloatABI::ABIType FloatABI, " | |
| "StringRef ABIName, LongDoubleFormat LongDoubleFormat) {\n"; | |
| for (const SystemLibraryDispatch &S : Systems) { | |
| OS << '\n'; | |
| AvailabilityPredicate TopLevelPredicate( | |
| S.System->getValueAsDef("TriplePred")); | |
| OS << indent(2); | |
| TopLevelPredicate.emitIf(OS); | |
| // The default is a property of the target, and is passed to the libraries | |
| // as an argument. | |
| StringRef DefaultCCArg = "CallingConv::C"; | |
| if (!S.DefaultCC.empty()) { | |
| OS << " const CallingConv::ID DefaultCC = " << S.DefaultCC << ";\n" | |
| << " for (CallingConv::ID &Entry : LibcallImplCallingConvs) {\n" | |
| " Entry = DefaultCC;\n" | |
| " }\n\n"; | |
| DefaultCCArg = "DefaultCC"; | |
| } | |
| // Dispatch each library to setAvailableLibFuncs_<FuncSuffix> under an | |
| // isLibraryAvailable(Name) guard. LibraryRef exclusions are applied inside | |
| // the library function. | |
| for (auto [Name, FuncSuffix] : S.Libs) { | |
| OS << indent(4) << "if (isLibraryAvailable("; | |
| emitRuntimeLibraryEnumerator(OS, Name); | |
| OS << "))\n" << indent(6) << "setAvailableLibFuncs_"; | |
| emitLibFuncSuffix(OS, FuncSuffix); | |
| OS << "(TT, ExceptionModel, FloatABI, ABIName, LongDoubleFormat, " | |
| << DefaultCCArg << ");\n"; | |
| } | |
| if (!S.Libs.empty()) | |
| OS << '\n'; | |
| OS << indent(4) << "return;\n" << indent(2); | |
| TopLevelPredicate.emitEndIf(OS); | |
| } | |
| // FIXME: This should be a fatal error. A few contexts are improperly relying | |
| // on RuntimeLibcalls constructed with fully unknown triples. | |
| OS << " LLVM_DEBUG(dbgs() << \"no system runtime library applied to target " | |
| "\\'\" << TT.str() << \"\\'\\n\");\n" | |
| "}\n\n"; | |
| } | |
| void RuntimeLibcallEmitter::emitLibraryByNameSetCalls( | |
| raw_ostream &OS, ArrayRef<SystemLibraryDispatch> Systems) const { | |
| // Libraries a SystemRuntimeLibrary uses are enabled by | |
| // setTargetRuntimeLibcallSets instead. | |
| DenseSet<StringRef> SystemLibNames; | |
| for (const SystemLibraryDispatch &S : Systems) { | |
| for (auto [LibName, FuncSuffix] : S.Libs) | |
| SystemLibNames.insert(LibName); | |
| } | |
| OS << "bool llvm::RTLIB::RuntimeLibcallsInfo::setAvailableLibraryLibcalls(" | |
| "RTLIB::RuntimeLibrary Library, const llvm::Triple &TT, " | |
| "ExceptionHandling ExceptionModel, FloatABI::ABIType FloatABI, " | |
| "StringRef ABIName, LongDoubleFormat LongDoubleFormat) {\n" | |
| " switch (Library) {\n"; | |
| for (const auto &[Name, Libs] : collectLibrariesByName()) { | |
| if (Libs.front()->getValueAsBit("Isolated") || | |
| SystemLibNames.contains(Name)) | |
| continue; | |
| OS << " case "; | |
| emitRuntimeLibraryEnumerator(OS, Name); | |
| OS << ":\n" << indent(4) << "setAvailableLibFuncs_"; | |
| emitLibFuncSuffix(OS, Name); | |
| OS << "(TT, ExceptionModel, FloatABI, ABIName, LongDoubleFormat, " | |
| "CallingConv::C);\n" | |
| << indent(4) << "return true;\n"; | |
| } | |
| OS << " default:\n" | |
| " return false;\n" | |
| " }\n" | |
| "}\n\n"; | |
| } | |
| // Scalar FP type suffixes in the argument order of RTLIB::getFPLibCall, paired | |
| // with the llvm::Type predicate used by the IR-level mapping. | |
| static constexpr std::pair<StringRef, StringRef> ScalarFPSuffixes[] = { | |
| {"F32", "isFloatTy()"}, {"F64", "isDoubleTy()"}, | |
| {"F80", "isX86_FP80Ty()"}, {"F128", "isFP128Ty()"}, | |
| {"PPCF128", "isPPC_FP128Ty()"}, | |
| }; | |
| static std::vector<FPLibcallFamily> | |
| collectFPLibcallFamilies(const RecordKeeper &Records) { | |
| std::vector<FPLibcallFamily> Families; | |
| for (const Record *R : | |
| Records.getAllDerivedDefinitions("RuntimeLibcallFamily")) | |
| Families.emplace_back(R); | |
| llvm::sort(Families, [](const FPLibcallFamily &A, const FPLibcallFamily &B) { | |
| return A.Base < B.Base; | |
| }); | |
| return Families; | |
| } | |
| DenseSet<StringRef> RuntimeLibcallEmitter::collectLibcallNames() const { | |
| DenseSet<StringRef> LibcallNames; | |
| for (const RuntimeLibcall &LC : Libcalls.getRuntimeLibcallDefList()) | |
| LibcallNames.insert(LC.getName()); | |
| return LibcallNames; | |
| } | |
| void RuntimeLibcallEmitter::checkFPLibcallFamilies( | |
| ArrayRef<FPLibcallFamily> Families, | |
| const DenseSet<StringRef> &LibcallNames) const { | |
| std::vector<std::pair<StringRef, StringRef>> IntrinsicToBase; | |
| for (const FPLibcallFamily &Family : Families) | |
| for (StringRef Intrinsic : Family.Intrinsics) | |
| IntrinsicToBase.emplace_back(Intrinsic, Family.Base); | |
| llvm::sort(IntrinsicToBase); | |
| for (size_t I = 1, E = IntrinsicToBase.size(); I < E; ++I) { | |
| if (IntrinsicToBase[I].first == IntrinsicToBase[I - 1].first) | |
| PrintFatalError( | |
| "intrinsic '" + IntrinsicToBase[I].first + | |
| "' is mapped by multiple RuntimeLibcallFamily records ('" + | |
| IntrinsicToBase[I - 1].second + "' and '" + | |
| IntrinsicToBase[I].second + "')"); | |
| } | |
| for (const FPLibcallFamily &Family : Families) { | |
| bool AnyScalarLibcall = any_of( | |
| ScalarFPSuffixes, [&](const std::pair<StringRef, StringRef> &Entry) { | |
| return LibcallNames.contains((Family.Base + "_" + Entry.first).str()); | |
| }); | |
| if (!AnyScalarLibcall) | |
| PrintFatalError("no runtime libcall found for base name '" + Family.Base + | |
| "'"); | |
| } | |
| } | |
| /// Generate the declarations for the RTLIB::get<base>(EVT) selectors. | |
| void RuntimeLibcallEmitter::emitFPLibcallSelectorDecls( | |
| raw_ostream &OS, ArrayRef<FPLibcallFamily> Families) const { | |
| IfDefEmitter IfDef(OS, "GET_RUNTIME_LIBCALL_FP_SELECTOR_DECLS"); | |
| for (const FPLibcallFamily &Family : Families) | |
| OS << "LLVM_ABI Libcall get" << Family.Base << "(EVT VT);\n"; | |
| } | |
| /// Generate the backend RTLIB::get<base>(EVT) selectors from the floating-point | |
| /// libcall families. | |
| void RuntimeLibcallEmitter::emitFPLibcallSelectors( | |
| raw_ostream &OS, ArrayRef<FPLibcallFamily> Families, | |
| const DenseSet<StringRef> &LibcallNames) const { | |
| IfDefEmitter IfDef(OS, "GET_RUNTIME_LIBCALL_FP_SELECTORS"); | |
| // Only emit a libcall enumerator if it actually exists in the declared set. | |
| auto scalarEnum = [&](StringRef Base, StringRef Suffix) -> std::string { | |
| if (LibcallNames.contains((Base + "_" + Suffix).str())) | |
| return ("RTLIB::" + Base + "_" + Suffix).str(); | |
| return "RTLIB::UNKNOWN_LIBCALL"; | |
| }; | |
| for (const FPLibcallFamily &Family : Families) { | |
| StringRef Base = Family.Base; | |
| OS << "RTLIB::Libcall llvm::RTLIB::get" << Base << "(EVT VT) {\n"; | |
| if (!Family.VectorSuffixes.empty()) { | |
| OS << " if (VT.isVector()) {\n" | |
| " if (!VT.isSimple())\n" | |
| " return RTLIB::UNKNOWN_LIBCALL;\n" | |
| " switch (VT.getSimpleVT().SimpleTy) {\n"; | |
| for (StringRef Suffix : Family.VectorSuffixes) { | |
| OS << " case MVT::" << Suffix.lower() | |
| << ":\n return RTLIB::" << Base << "_" << Suffix << ";\n"; | |
| } | |
| OS << " default:\n" | |
| " return RTLIB::UNKNOWN_LIBCALL;\n" | |
| " }\n" | |
| " }\n"; | |
| } | |
| OS << " return getFPLibCall(VT"; | |
| for (auto [Suffix, Pred] : ScalarFPSuffixes) | |
| OS << ", " << scalarEnum(Base, Suffix); | |
| OS << ");\n}\n\n"; | |
| } | |
| } | |
| /// Emit the mapping from floating-point math intrinsics to the runtime libcall | |
| /// they may lower to, keyed by intrinsic ID and floating-point type. This is | |
| /// the IR-level counterpart to the backend's RTLIB::getXXX(EVT) selectors. | |
| void RuntimeLibcallEmitter::emitGetLibcallForIntrinsic( | |
| raw_ostream &OS, ArrayRef<FPLibcallFamily> Families, | |
| const DenseSet<StringRef> &LibcallNames) const { | |
| IfDefEmitter IfDef(OS, "GET_RUNTIME_LIBCALL_INTRINSIC_TO_LIBCALL"); | |
| std::vector<std::pair<StringRef, StringRef>> IntrinsicToBase; | |
| for (const FPLibcallFamily &Family : Families) | |
| for (StringRef Intrinsic : Family.Intrinsics) | |
| IntrinsicToBase.emplace_back(Intrinsic, Family.Base); | |
| llvm::sort(IntrinsicToBase); | |
| MapVector<StringRef, SmallVector<StringRef, 2>> BaseToIntrinsics; | |
| for (auto [Intrinsic, Base] : IntrinsicToBase) | |
| BaseToIntrinsics[Base].push_back(Intrinsic); | |
| OS << "RTLIB::Libcall " | |
| "llvm::RTLIB::RuntimeLibcallsInfo::getLibcallForIntrinsic(" | |
| "Intrinsic::ID ID, FunctionType *FTy) {\n" | |
| " Type *Ty = FTy->getReturnType();\n" | |
| " if (!Ty->isFloatingPointTy()) {\n" | |
| " for (Type *ParamTy : FTy->params()) {\n" | |
| " if (ParamTy->isFloatingPointTy()) {\n" | |
| " Ty = ParamTy;\n" | |
| " break;\n" | |
| " }\n" | |
| " }\n" | |
| " }\n" | |
| " if (!Ty->isFloatingPointTy())\n" | |
| " return RTLIB::UNKNOWN_LIBCALL;\n" | |
| " switch (ID) {\n"; | |
| for (const auto &[Base, Intrinsics] : BaseToIntrinsics) { | |
| SmallVector<std::pair<StringRef, StringRef>, 5> Arms; | |
| for (auto [Suffix, Pred] : ScalarFPSuffixes) | |
| if (LibcallNames.contains((Base + "_" + Suffix).str())) | |
| Arms.emplace_back(Suffix, Pred); | |
| for (StringRef Intrinsic : Intrinsics) | |
| OS << " case Intrinsic::" << Intrinsic << ":\n"; | |
| for (auto [Suffix, Pred] : Arms) | |
| OS << " if (Ty->" << Pred << ")\n return RTLIB::" << Base << "_" | |
| << Suffix << ";\n"; | |
| OS << " return RTLIB::UNKNOWN_LIBCALL;\n"; | |
| } | |
| OS << " default:\n" | |
| " return RTLIB::UNKNOWN_LIBCALL;\n" | |
| " }\n" | |
| "}\n"; | |
| } | |
| void RuntimeLibcallEmitter::run(raw_ostream &OS) { | |
| emitSourceFileHeader("Runtime LibCalls Source Fragment", OS, Records); | |
| emitGetRuntimeLibcallEnum(OS); | |
| emitGetInitRuntimeLibcallNames(OS); | |
| emitGetInitRuntimeLibcallSignatures(OS); | |
| emitMaskedVectorLibcallMapping(OS); | |
| emitRuntimeLibcallsInfoMemberDecls(OS); | |
| { | |
| IfDefEmitter IfDef(OS, "GET_RUNTIME_LIBCALLS_INFO"); | |
| std::vector<SystemLibraryDispatch> Systems = collectSystemLibraryDispatch(); | |
| emitSystemRuntimeLibrarySetCalls(OS, Systems); | |
| emitLibraryByNameSetCalls(OS, Systems); | |
| } | |
| std::vector<FPLibcallFamily> FPFamilies = collectFPLibcallFamilies(Records); | |
| DenseSet<StringRef> LibcallNames = collectLibcallNames(); | |
| checkFPLibcallFamilies(FPFamilies, LibcallNames); | |
| emitFPLibcallSelectorDecls(OS, FPFamilies); | |
| emitFPLibcallSelectors(OS, FPFamilies, LibcallNames); | |
| emitGetLibcallForIntrinsic(OS, FPFamilies, LibcallNames); | |
| } | |
| static TableGen::Emitter::OptClass<RuntimeLibcallEmitter> | |
| X("gen-runtime-libcalls", "Generate RuntimeLibcalls"); |