https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/217592
>From 34ec4932e165bb16b292cce8806f7a60bf1ac0ae Mon Sep 17 00:00:00 2001 From: Matt Arsenault <[email protected]> Date: Thu, 13 Aug 2026 16:11:17 +0200 Subject: [PATCH] RuntimeLibcalls: Introduce LibcallLibrary schema Currently the set of system libraries calls is flat and disorganized. Begin organizing this per-provider library. The goal is to organize groups of functions by named sets, corresponding to the underlying library which will be linked. A LibcallLibrary is a named runtime library whose impls are made available as a unit; its members use the same dag vocabulary as LibcallImpls. The emitter emits one setAvailableLibFuncs_<name> per distinct library name, merging same-named libraries under their per-variant availability predicates. isLibraryAvailable() is added as a stub for a future dispatch driver. No target defines a LibcallLibrary, so generated output is mostly unchanged (there are some incidental enum reorderings). Reorganizing all of the library functions require a good bit more infrastructure to be practical, but this is a minimally functional piece to start the review. Co-authored-by: Claude (Claude-Opus-4.8) <[email protected]> --- llvm/include/llvm/IR/RuntimeLibcalls.h | 4 + llvm/include/llvm/IR/RuntimeLibcallsImpl.td | 12 + llvm/lib/IR/RuntimeLibcalls.cpp | 6 + .../RuntimeLibcallEmitter-library-grouping.td | 80 +++++ ...untimeLibcallEmitter-library-name-merge.td | 102 ++++++ .../RuntimeLibcallEmitter-multiple-impls.td | 4 +- .../TableGen/Basic/RuntimeLibcallsEmitter.cpp | 336 +++++++++++++----- 7 files changed, 459 insertions(+), 85 deletions(-) create mode 100644 llvm/test/TableGen/RuntimeLibcallEmitter-library-grouping.td create mode 100644 llvm/test/TableGen/RuntimeLibcallEmitter-library-name-merge.td diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.h b/llvm/include/llvm/IR/RuntimeLibcalls.h index 4def7c6db3d50..e7dfec449a842 100644 --- a/llvm/include/llvm/IR/RuntimeLibcalls.h +++ b/llvm/include/llvm/IR/RuntimeLibcalls.h @@ -234,6 +234,10 @@ struct RuntimeLibcallsInfo { LLVM_READONLY static bool isAAPCS_ABI(const Triple &TT, StringRef ABIName); + /// Return whether the runtime library \p LibraryName is available for the + /// current module. + bool isLibraryAvailable(StringRef LibraryName) const; + /// Generated by tablegen. void setTargetRuntimeLibcallSets(const Triple &TT, ExceptionHandling ExceptionModel, diff --git a/llvm/include/llvm/IR/RuntimeLibcallsImpl.td b/llvm/include/llvm/IR/RuntimeLibcallsImpl.td index 1c49959285f08..59616efb41c57 100644 --- a/llvm/include/llvm/IR/RuntimeLibcallsImpl.td +++ b/llvm/include/llvm/IR/RuntimeLibcallsImpl.td @@ -113,6 +113,18 @@ class LibcallsWithCC<dag funcList, LibcallCallingConv CC, LibcallCallingConv CallingConv = CC; } +// A named runtime library whose Impls are made available as a unit. Impls use +// the same dag vocabulary as SystemRuntimeLibrary. +class LibcallLibrary<string name, dag impls, + RuntimeLibcallAvailability pred = AlwaysAvailable> { + string LibraryName = name; + dag Impls = impls; + + // Availability guard for the library. Libraries sharing a LibraryName merge + // into one function, each variant under its own Pred. + RuntimeLibcallAvailability Pred = pred; +} + /// Define a complete top level set of runtime libcalls for a target. class SystemRuntimeLibrary<RuntimeLibcallAvailability Pred, dag funcList> { /// Set the default calling convention assumed for RuntimeLibcallImpl members. diff --git a/llvm/lib/IR/RuntimeLibcalls.cpp b/llvm/lib/IR/RuntimeLibcalls.cpp index 1befff9d5f00e..67e1bacdb329d 100644 --- a/llvm/lib/IR/RuntimeLibcalls.cpp +++ b/llvm/lib/IR/RuntimeLibcalls.cpp @@ -113,6 +113,12 @@ RuntimeLibcallsInfo::RuntimeLibcallsInfo(const Module &M, : RuntimeLibcallsInfo(M.getTargetTriple(), ExceptionModel, M.getFloatABI(), EABIVersion, ABIName, VecLib) {} +bool RuntimeLibcallsInfo::isLibraryAvailable(StringRef LibraryName) const { + // TODO: Drive this from module-level state (e.g. the linked runtime). For now + // every named library is reported as available. + return true; +} + /// Set default libcall names. If a target wants to opt-out of a libcall it /// should be placed here. void RuntimeLibcallsInfo::initLibcalls(const Triple &TT, diff --git a/llvm/test/TableGen/RuntimeLibcallEmitter-library-grouping.td b/llvm/test/TableGen/RuntimeLibcallEmitter-library-grouping.td new file mode 100644 index 0000000000000..6937b935e7494 --- /dev/null +++ b/llvm/test/TableGen/RuntimeLibcallEmitter-library-grouping.td @@ -0,0 +1,80 @@ +// RUN: llvm-tblgen -gen-runtime-libcalls -I %p/../../include %s | FileCheck %s + +// Check that each LibcallLibrary emits a standalone implementation function +// (suffixed by the library name) that makes the library's members available, +// with per-member triple predicates nested inside. + +include "llvm/IR/RuntimeLibcallsImpl.td" + +def MEMCPY : RuntimeLibcall; +def MEMSET : RuntimeLibcall; +def SQRT_F64 : RuntimeLibcall; +def SINCOS_F64 : RuntimeLibcall; + +def memcpy : RuntimeLibcallImpl<MEMCPY>; +def memset_pattern : RuntimeLibcallImpl<MEMSET, "memset_pattern">; +def sqrt : RuntimeLibcallImpl<SQRT_F64, "sqrt">; +def sincos : RuntimeLibcallImpl<SINCOS_F64, "sincos">; + +def IsDarwin : LibcallPredicate<[{TT.isOSDarwin()}]>; +def IsX86 : LibcallPredicate<[{TT.isX86()}]>; +def isDarwin : RuntimeLibcallAvailability<(all_of IsDarwin)>; +def isX86 : RuntimeLibcallAvailability<(all_of IsX86)>; + +// libc: memcpy is universal; memset_pattern only on Darwin. +def Libc : LibcallLibrary<"libc", (add memcpy, + LibcallImpls<(add memset_pattern), isDarwin>)>; + +// libm: sqrt is universal; sincos only on x86. +def Libm : LibcallLibrary<"libm", (add sqrt, + LibcallImpls<(add sincos), isX86>)>; + +// One standalone implementation function is emitted per LibcallLibrary. + +// CHECK: static void setAvailableLibFuncs_libc(llvm::RTLIB::RuntimeLibcallsInfo &Info, const llvm::Triple &TT, ExceptionHandling ExceptionModel, FloatABI::ABIType FloatABI, EABI EABIVersion, StringRef ABIName, LongDoubleFormat LongDoubleFormat) { +// CHECK-NEXT: { +// CHECK-NEXT: static const RTLIB::LibcallImpl LibraryCalls[] = { +// CHECK-NEXT: RTLIB::impl_memcpy, // memcpy +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK-NEXT: for (const RTLIB::LibcallImpl Impl : LibraryCalls) { +// CHECK-NEXT: Info.setAvailable(Impl); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK-NEXT: if (TT.isOSDarwin()) { +// CHECK-NEXT: static const RTLIB::LibcallImpl LibraryCalls_isDarwin[] = { +// CHECK-NEXT: RTLIB::impl_memset_pattern, // memset_pattern +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK-NEXT: for (const RTLIB::LibcallImpl Impl : LibraryCalls_isDarwin) { +// CHECK-NEXT: Info.setAvailable(Impl); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK-NEXT: } +// CHECK-NEXT: } + +// CHECK: static void setAvailableLibFuncs_libm(llvm::RTLIB::RuntimeLibcallsInfo &Info, const llvm::Triple &TT, ExceptionHandling ExceptionModel, FloatABI::ABIType FloatABI, EABI EABIVersion, StringRef ABIName, LongDoubleFormat LongDoubleFormat) { +// CHECK-NEXT: { +// CHECK-NEXT: static const RTLIB::LibcallImpl LibraryCalls[] = { +// CHECK-NEXT: RTLIB::impl_sqrt, // sqrt +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK-NEXT: for (const RTLIB::LibcallImpl Impl : LibraryCalls) { +// CHECK-NEXT: Info.setAvailable(Impl); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK-NEXT: if (TT.isX86()) { +// CHECK-NEXT: static const RTLIB::LibcallImpl LibraryCalls_isX86[] = { +// CHECK-NEXT: RTLIB::impl_sincos, // sincos +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK-NEXT: for (const RTLIB::LibcallImpl Impl : LibraryCalls_isX86) { +// CHECK-NEXT: Info.setAvailable(Impl); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/llvm/test/TableGen/RuntimeLibcallEmitter-library-name-merge.td b/llvm/test/TableGen/RuntimeLibcallEmitter-library-name-merge.td new file mode 100644 index 0000000000000..06b900025d230 --- /dev/null +++ b/llvm/test/TableGen/RuntimeLibcallEmitter-library-name-merge.td @@ -0,0 +1,102 @@ +// RUN: llvm-tblgen -gen-runtime-libcalls -I %p/../../include %s | FileCheck %s + +// Several libraries sharing one linker name (a GNU and a Darwin "libc") merge +// into a single setAvailableLibFuncs_libc. The shared core is emitted once, +// unguarded; each variant's guarded block carries only its own extras. + +include "llvm/IR/RuntimeLibcallsImpl.td" + +def MEMCPY : RuntimeLibcall; +def MEMSET : RuntimeLibcall; +def BZERO : RuntimeLibcall; +def STPCPY : RuntimeLibcall; + +def memcpy : RuntimeLibcallImpl<MEMCPY>; +def memset : RuntimeLibcallImpl<MEMSET>; +def bzero : RuntimeLibcallImpl<BZERO>; +def stpcpy : RuntimeLibcallImpl<STPCPY>; + +def IsDarwin : LibcallPredicate<[{TT.isOSDarwin()}]>; +def IsLinux : LibcallPredicate<[{TT.isOSLinux()}]>; +def isDarwin : RuntimeLibcallAvailability<(all_of IsDarwin)>; +def isLinux : RuntimeLibcallAvailability<(all_of IsLinux)>; + +def SQRT_F64 : RuntimeLibcall; +def sqrt : RuntimeLibcallImpl<SQRT_F64, "sqrt">; + +// The core libc functions shared by every libc variant. +defvar CoreLibc = (add memcpy, memset); + +// Each system's libc = the common core + its own extras, selected by Pred. +def GnuLibc : LibcallLibrary<"libc", (add CoreLibc, stpcpy)> { let Pred = isLinux; } +def DarwinLibc : LibcallLibrary<"libc", (add CoreLibc, bzero)> { let Pred = isDarwin; } + +// sqrt appears in both variants but with a different calling convention +// (ARM_AAPCS on GNU, default on Darwin), so it must not be hoisted into the +// shared core (that would drop the per-target CC). +def GnuLibm : LibcallLibrary<"libm", (add LibcallsWithCC<(add sqrt), ARM_AAPCS>)> { + let Pred = isLinux; +} +def DarwinLibm : LibcallLibrary<"libm", (add sqrt)> { let Pred = isDarwin; } + +// The shared core (memcpy, memset) is emitted once and unguarded; each variant's +// guarded block carries only its own extras (bzero for Darwin, stpcpy for GNU). + +// CHECK: static void setAvailableLibFuncs_libc(llvm::RTLIB::RuntimeLibcallsInfo &Info, const llvm::Triple &TT, ExceptionHandling ExceptionModel, FloatABI::ABIType FloatABI, EABI EABIVersion, StringRef ABIName, LongDoubleFormat LongDoubleFormat) { +// CHECK-NEXT: static const RTLIB::LibcallImpl LibraryCalls[] = { +// CHECK-NEXT: RTLIB::impl_memcpy, // memcpy +// CHECK-NEXT: RTLIB::impl_memset, // memset +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK-NEXT: for (const RTLIB::LibcallImpl Impl : LibraryCalls) { +// CHECK-NEXT: Info.setAvailable(Impl); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK-NEXT: if (TT.isOSDarwin()) { +// CHECK-NEXT: static const RTLIB::LibcallImpl LibraryCalls[] = { +// CHECK-NEXT: RTLIB::impl_bzero, // bzero +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK-NEXT: for (const RTLIB::LibcallImpl Impl : LibraryCalls) { +// CHECK-NEXT: Info.setAvailable(Impl); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK-NEXT: } +// CHECK-NEXT: if (TT.isOSLinux()) { +// CHECK-NEXT: static const RTLIB::LibcallImpl LibraryCalls[] = { +// CHECK-NEXT: RTLIB::impl_stpcpy, // stpcpy +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK-NEXT: for (const RTLIB::LibcallImpl Impl : LibraryCalls) { +// CHECK-NEXT: Info.setAvailable(Impl); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK-NEXT: } +// CHECK-NEXT: } + +// sqrt differs in calling convention between the two libm variants, so it stays +// per-variant: no shared-core hoist, and the GNU variant keeps its ARM_AAPCS. + +// CHECK: static void setAvailableLibFuncs_libm(llvm::RTLIB::RuntimeLibcallsInfo &Info, const llvm::Triple &TT, ExceptionHandling ExceptionModel, FloatABI::ABIType FloatABI, EABI EABIVersion, StringRef ABIName, LongDoubleFormat LongDoubleFormat) { +// CHECK-NEXT: if (TT.isOSDarwin()) { +// CHECK-NEXT: static const RTLIB::LibcallImpl LibraryCalls[] = { +// CHECK-NEXT: RTLIB::impl_sqrt, // sqrt +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK-NEXT: for (const RTLIB::LibcallImpl Impl : LibraryCalls) { +// CHECK-NEXT: Info.setAvailable(Impl); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK-NEXT: } +// CHECK-NEXT: if (TT.isOSLinux()) { +// CHECK-NEXT: static const RTLIB::LibcallImpl LibraryCalls_AlwaysAvailable_ARM_AAPCS[] = { +// CHECK-NEXT: RTLIB::impl_sqrt, // sqrt +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK-NEXT: for (const RTLIB::LibcallImpl Impl : LibraryCalls_AlwaysAvailable_ARM_AAPCS) { +// CHECK-NEXT: Info.setAvailable(Impl); +// CHECK-NEXT: Info.setLibcallImplCallingConv(Impl, CallingConv::ARM_AAPCS); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/llvm/test/TableGen/RuntimeLibcallEmitter-multiple-impls.td b/llvm/test/TableGen/RuntimeLibcallEmitter-multiple-impls.td index 640b3ba7e5435..4fd713bd158dc 100644 --- a/llvm/test/TableGen/RuntimeLibcallEmitter-multiple-impls.td +++ b/llvm/test/TableGen/RuntimeLibcallEmitter-multiple-impls.td @@ -37,8 +37,8 @@ def dup1 : RuntimeLibcallImpl<ANOTHER_DUP>; // CHECK-EMPTY: // CHECK-NEXT: static const RTLIB::LibcallImpl LibraryCalls[] = { -// CHECK-NEXT: RTLIB::impl_func_b, // func_b // CHECK-NEXT: RTLIB::impl_func_a, // func_a +// CHECK-NEXT: RTLIB::impl_func_b, // func_b // CHECK-NEXT: }; // CHECK-EMPTY: // CHECK-NEXT: for (const RTLIB::LibcallImpl Impl : LibraryCalls) { @@ -82,8 +82,8 @@ def TheSystemLibraryB : SystemRuntimeLibrary<isTargetArchB, // CHECK-NEXT: AvailableLibcallImpls = SystemAvailableImpls; // CHECK-EMPTY: // CHECK-NEXT: static const RTLIB::LibcallImpl LibraryCalls[] = { -// CHECK-NEXT: RTLIB::impl_dup1, // dup1 // CHECK-NEXT: RTLIB::impl_dup0, // dup0 +// CHECK-NEXT: RTLIB::impl_dup1, // dup1 // CHECK-NEXT: RTLIB::impl_other_func, // other_func // CHECK-NEXT: RTLIB::impl_func_a, // func_a // CHECK-NEXT: RTLIB::impl_func_b, // func_b diff --git a/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp b/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp index 7a76c1740b53b..2dbff92f623c0 100644 --- a/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp +++ b/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp @@ -12,6 +12,7 @@ #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" @@ -87,6 +88,21 @@ class RuntimeLibcallEmitter { 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)`. \p Receiver prefixes the calls ("Info." for the + // standalone library functions, empty otherwise). + void + emitPredicateGroups(raw_ostream &OS, const Record *R, + DenseMap<PredicateWithCC, LibcallsWithCC> &Pred2Funcs, + SetVector<PredicateWithCC> &PredicateSorter, + unsigned BaseIndent, StringRef Receiver) const; + + // Emit a file-local `setAvailableLibFuncs_<name>` for all LibcallLibrary defs + // sharing \p Name, each gated by its own availability predicate. + void emitLibraryFunction(raw_ostream &OS, StringRef Name, + ArrayRef<const Record *> Libs) const; + void emitSystemRuntimeLibrarySetCalls(raw_ostream &OS) const; DenseSet<StringRef> collectLibcallNames() const; @@ -371,16 +387,248 @@ const uint8_t RTLIB::RuntimeLibcallsInfo::RuntimeLibcallNameSizeTable[] = { emitNameMatchHashTable(OS, Table); } +void RuntimeLibcallEmitter::emitPredicateGroups( + raw_ostream &OS, const Record *R, + DenseMap<PredicateWithCC, LibcallsWithCC> &Pred2Funcs, + SetVector<PredicateWithCC> &PredicateSorter, unsigned BaseIndent, + StringRef Receiver) 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. + 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) << Receiver << "setAvailable(Impl);\n"; + + if (FuncsWithCC.CallingConv) { + StringRef CCEnum = + FuncsWithCC.CallingConv->getValueAsString("CallingConv"); + OS << indent(IndentDepth + 4) << Receiver + << "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::emitLibraryFunction( + raw_ostream &OS, StringRef Name, ArrayRef<const Record *> Libs) const { + // File-local; referenced only from the driver in the same fragment. + OS << "static void setAvailableLibFuncs_"; + emitLibFuncSuffix(OS, Name); + OS << "(llvm::RTLIB::RuntimeLibcallsInfo &Info, const llvm::Triple &TT, " + "ExceptionHandling ExceptionModel, FloatABI::ABIType FloatABI, " + "EABI EABIVersion, StringRef ABIName, " + "LongDoubleFormat LongDoubleFormat) {\n"; + + // Per-variant expansion. Unconditional impls are tracked separately for + // cross-variant deduplication. + struct ExpandedLibrary { + const Record *Lib; + DenseMap<PredicateWithCC, LibcallsWithCC> Pred2Funcs; + SetVector<PredicateWithCC> PredicateSorter; + SetVector<const RuntimeLibcallImpl *> Unconditional; + }; + + 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, /*Receiver=*/"Info."); + + for (ExpandedLibrary &EL : Expanded) { + auto &Funcs = EL.Pred2Funcs[PredicateWithCC()].LibcallImpls; + llvm::erase_if(Funcs, [&](const RuntimeLibcallImpl *Impl) { + return SharedCore.contains(Impl); + }); + } + } + + // Emit each variant under its own Pred. + for (ExpandedLibrary &EL : Expanded) { + 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, /*Receiver=*/"Info."); + + if (!LibPred.isAlwaysAvailable()) { + OS << indent(2); + LibPred.emitEndIf(OS); + } else { + OS << indent(2) << "}\n"; + } + } + + OS << "}\n\n"; +} + void RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls( raw_ostream &OS) const { + // Emit one function per distinct library name; same-named defs merge. + // + // TODO: SystemRuntimeLibrary does not yet dispatch to these + MapVector<StringRef, std::vector<const Record *>> LibsByName; + for (const Record *Lib : Records.getAllDerivedDefinitions("LibcallLibrary")) + LibsByName[Lib->getValueAsString("LibraryName")].push_back(Lib); + + for (const auto &[Name, Libs] : LibsByName) + emitLibraryFunction(OS, Name, Libs); + + ArrayRef<const Record *> AllLibs = + Records.getAllDerivedDefinitions("SystemRuntimeLibrary"); + OS << "void llvm::RTLIB::RuntimeLibcallsInfo::setTargetRuntimeLibcallSets(" "const llvm::Triple &TT, ExceptionHandling ExceptionModel, " "FloatABI::ABIType FloatABI, EABI EABIVersion, " "StringRef ABIName, LongDoubleFormat LongDoubleFormat) {\n"; - ArrayRef<const Record *> AllLibs = - Records.getAllDerivedDefinitions("SystemRuntimeLibrary"); - for (const Record *R : AllLibs) { OS << '\n'; @@ -474,86 +722,8 @@ void RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls( OS << "\n });\n" " AvailableLibcallImpls = SystemAvailableImpls;\n\n"; - 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 ties on the calling convention so predicates that share a name - // but differ in calling convention emit in 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 = 2; - - auto It = Pred2Funcs.find(Entry); - if (It == Pred2Funcs.end()) - continue; - - if (!SubsetPredicate.isAlwaysAvailable()) { - IndentDepth = 4; - - OS << indent(IndentDepth); - SubsetPredicate.emitIf(OS); - } - - LibcallsWithCC &FuncsWithCC = It->second; - - std::vector<const RuntimeLibcallImpl *> &Funcs = FuncsWithCC.LibcallImpls; - - // This table records which implementations are available, not which one - // is selected, so a libcall may legitimately have more than one available - // implementation - stable_sort(Funcs, [](const RuntimeLibcallImpl *A, - const RuntimeLibcallImpl *B) { - return A->getProvides()->getEnumVal() < B->getProvides()->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'; - } - } + emitPredicateGroups(OS, R, Pred2Funcs, PredicateSorter, /*BaseIndent=*/2, + /*Receiver=*/""); OS << indent(4) << "return;\n" << indent(2); TopLevelPredicate.emitEndIf(OS); _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
