[llvm-branch-commits] [llvm] RuntimeLibcalls: Introduce LibcallLibrary schema (PR #217592)
Matt Arsenault via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Aug 20 04:52:37 PDT 2026
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 <Matthew.Arsenault at amd.com>
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) <noreply at anthropic.com>
---
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);
More information about the llvm-branch-commits
mailing list