| //===- 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 "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; | |
| // 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 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; | |
| void emitRuntimeLibcallsInfoMemberDecls(raw_ostream &OS) const; | |
| void emitSystemRuntimeLibrarySetCalls(raw_ostream &OS) 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. | |
| 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" | |
| "} // 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); | |
| } | |
| 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'; | |
| } | |
| } | |
| } | |
| // 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 : '_'); | |
| } | |
| 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) {\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"; | |
| } | |
| MapVector<StringRef, std::vector<const Record *>> | |
| RuntimeLibcallEmitter::collectLibrariesByName() const { | |
| MapVector<StringRef, std::vector<const Record *>> LibsByName; | |
| for (const Record *Lib : Records.getAllDerivedDefinitions("LibcallLibrary")) | |
| LibsByName[Lib->getValueAsString("LibraryName")].push_back(Lib); | |
| return LibsByName; | |
| } | |
| 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);\n"; | |
| } | |
| } | |
| void RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls( | |
| raw_ostream &OS) const { | |
| ArrayRef<const Record *> AllLibs = | |
| Records.getAllDerivedDefinitions("SystemRuntimeLibrary"); | |
| // 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 Record *R : AllLibs) { | |
| 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 Record *R : AllLibs) { | |
| OS << '\n'; | |
| AvailabilityPredicate TopLevelPredicate(R->getValueAsDef("TriplePred")); | |
| OS << indent(2); | |
| TopLevelPredicate.emitIf(OS); | |
| if (const Record *DefaultCCClass = | |
| R->getValueAsDef("DefaultLibcallCallingConv")) { | |
| StringRef DefaultCC = | |
| DefaultCCClass->getValueAsString("CallingConv").trim(); | |
| if (!DefaultCC.empty()) { | |
| OS << " const CallingConv::ID DefaultCC = " << DefaultCC << ";\n" | |
| << " for (CallingConv::ID &Entry : LibcallImplCallingConvs) {\n" | |
| " Entry = DefaultCC;\n" | |
| " }\n\n"; | |
| } | |
| } | |
| // Split the top-level member list into named LibcallLibrary references | |
| // (dispatched to their own setAvailableLibFuncs_<name> under an | |
| // isLibraryAvailable guard) and the remaining bare impl / LibcallImpls | |
| // members. A LibraryRef also records impls to drop. | |
| struct DispatchLib { | |
| StringRef Name; | |
| std::vector<const RuntimeLibcallImpl *> Exclude; | |
| }; | |
| const DagInit *MemberDag = | |
| R->getValueAsDef("MemberList")->getValueAsDag("MemberList"); | |
| SmallVector<DispatchLib, 4> DispatchLibs; | |
| SmallVector<const Init *, 16> InlineArgs; | |
| SmallVector<const StringInit *, 16> InlineArgNames; | |
| for (auto [Arg, ArgName] : | |
| zip_equal(MemberDag->getArgs(), MemberDag->getArgNames())) { | |
| if (const auto *DI = dyn_cast<DefInit>(Arg)) { | |
| const Record *Def = DI->getDef(); | |
| if (Def->isSubClassOf("LibcallLibrary")) { | |
| DispatchLibs.push_back({Def->getValueAsString("LibraryName"), {}}); | |
| continue; | |
| } | |
| if (Def->isSubClassOf("LibraryRef")) { | |
| const Record *Lib = Def->getValueAsDef("Library"); | |
| DispatchLib DL{Lib->getValueAsString("LibraryName"), {}}; | |
| for (const Record *ExcludeRec : | |
| Def->getValueAsListOfDefs("Exclude")) { | |
| if (const RuntimeLibcallImpl *Impl = | |
| Libcalls.getRuntimeLibcallImpl(ExcludeRec)) | |
| DL.Exclude.push_back(Impl); | |
| } | |
| DispatchLibs.push_back(std::move(DL)); | |
| continue; | |
| } | |
| } | |
| InlineArgs.push_back(Arg); | |
| InlineArgNames.push_back(ArgName); | |
| } | |
| const DagInit *InlineDag = | |
| DagInit::get(MemberDag->getOperator(), InlineArgs, InlineArgNames); | |
| 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 ElementsSet; | |
| Sets.evaluate(InlineDag, ElementsSet, R->getLoc()); | |
| const SetTheory::RecSet *Elements = &ElementsSet; | |
| // Sort to get deterministic output | |
| SetVector<PredicateWithCC> PredicateSorter; | |
| PredicateSorter.insert( | |
| PredicateWithCC()); // No predicate or CC override first. | |
| constexpr unsigned BitsPerStorageElt = 64; | |
| DenseMap<PredicateWithCC, LibcallsWithCC> Pred2Funcs; | |
| SmallVector<uint64_t, 32> BitsetValues(divideCeil( | |
| Libcalls.getRuntimeLibcallImplDefList().size() + 1, BitsPerStorageElt)); | |
| for (const Record *Elt : *Elements) { | |
| const RuntimeLibcallImpl *LibCallImpl = | |
| Libcalls.getRuntimeLibcallImpl(Elt); | |
| if (!LibCallImpl) { | |
| PrintError(R, "entry for SystemLibrary is not a RuntimeLibcallImpl"); | |
| PrintNote(Elt->getLoc(), "invalid entry `" + Elt->getName() + "`"); | |
| continue; | |
| } | |
| size_t BitIdx = LibCallImpl->getEnumVal(); | |
| uint64_t BitmaskVal = uint64_t(1) << (BitIdx % BitsPerStorageElt); | |
| size_t BitsetIdx = BitIdx / BitsPerStorageElt; | |
| auto It = Func2Preds.find(LibCallImpl); | |
| if (It == Func2Preds.end()) { | |
| BitsetValues[BitsetIdx] |= BitmaskVal; | |
| Pred2Funcs[PredicateWithCC()].LibcallImpls.push_back(LibCallImpl); | |
| continue; | |
| } | |
| for (const Record *Pred : It->second.first) { | |
| const Record *CC = It->second.second; | |
| AvailabilityPredicate SubsetPredicate(Pred); | |
| if (SubsetPredicate.isAlwaysAvailable()) | |
| BitsetValues[BitsetIdx] |= BitmaskVal; | |
| PredicateWithCC Key(Pred, CC); | |
| auto &Entry = Pred2Funcs[Key]; | |
| Entry.LibcallImpls.push_back(LibCallImpl); | |
| Entry.CallingConv = It->second.second; | |
| PredicateSorter.insert(Key); | |
| } | |
| } | |
| OS << " static constexpr LibcallImplBitset SystemAvailableImpls({\n" | |
| << indent(6); | |
| ListSeparator LS; | |
| unsigned EntryCount = 0; | |
| for (uint64_t Bits : BitsetValues) { | |
| if (EntryCount++ == 4) { | |
| EntryCount = 1; | |
| OS << ",\n" << indent(6); | |
| } else | |
| OS << LS; | |
| OS << format_hex(Bits, 16); | |
| } | |
| OS << "\n });\n" | |
| " AvailableLibcallImpls = SystemAvailableImpls;\n\n"; | |
| // Dispatch to each named library's setup function. This must come after the | |
| // SystemAvailableImpls assignment above (which overwrites the bitset); the | |
| // library functions union their members in on top via setAvailable. | |
| for (const DispatchLib &DL : DispatchLibs) { | |
| OS << indent(4) << "if (isLibraryAvailable(\"" << DL.Name << "\"))\n" | |
| << indent(6) << "setAvailableLibFuncs_"; | |
| emitLibFuncSuffix(OS, DL.Name); | |
| OS << "(TT, ExceptionModel, FloatABI, ABIName, LongDoubleFormat);\n"; | |
| } | |
| if (!DispatchLibs.empty()) | |
| OS << '\n'; | |
| emitPredicateGroups(OS, R, Pred2Funcs, PredicateSorter, /*BaseIndent=*/2); | |
| 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"; | |
| } | |
| // 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); | |
| emitRuntimeLibcallsInfoMemberDecls(OS); | |
| { | |
| IfDefEmitter IfDef(OS, "GET_RUNTIME_LIBCALLS_INFO"); | |
| emitSystemRuntimeLibrarySetCalls(OS); | |
| } | |
| 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"); |