[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:58:39 PDT 2026


https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/217592

>From 402650e140d4552430c3bd5c5be24cd603d58207 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