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

>From 81235406698f1fbfb2fc2cd607aa20bfe37d156c Mon Sep 17 00:00:00 2001
From: Matt Arsenault <[email protected]>
Date: Fri, 14 Aug 2026 13:37:23 +0200
Subject: [PATCH] RuntimeLibcallsEmitter: Add isolated LibcallLibrary variant

This is essentially a hack to not break the common core functions shared
by most targets. Most targets have essentially the same base set of
compiler-rt or libc/libm functions, but a few are so radically different
there is nothing in common (e.g., the GPU targets have a handful of functions,
arm64ec changes every single function). This bit will pull these out of the
library merge-by-name system, and emitted as its own special case. This allows
the single name to be universal across targets without duplicating large
tables in the emitted inc file.

Co-authored-by: Claude (Opus 4.8) <[email protected]>
---
 llvm/include/llvm/IR/RuntimeLibcallsImpl.td   |  7 ++
 .../RuntimeLibcallEmitter-library-isolated.td | 74 +++++++++++++++++++
 .../TableGen/Basic/RuntimeLibcallsEmitter.cpp | 22 ++++--
 3 files changed, 98 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/TableGen/RuntimeLibcallEmitter-library-isolated.td

diff --git a/llvm/include/llvm/IR/RuntimeLibcallsImpl.td 
b/llvm/include/llvm/IR/RuntimeLibcallsImpl.td
index 516cf0515a1035f..c8255913a9d21eb 100644
--- a/llvm/include/llvm/IR/RuntimeLibcallsImpl.td
+++ b/llvm/include/llvm/IR/RuntimeLibcallsImpl.td
@@ -123,6 +123,13 @@ class LibcallLibrary<string name, dag impls,
   // Availability guard for the library. Libraries sharing a LibraryName merge
   // into one function, each variant under its own Pred.
   RuntimeLibcallAvailability Pred = pred;
+
+  // When set, emit this library as its own setup function instead of merging 
it
+  // with others sharing its LibraryName, so a consumer gets exactly these 
members
+  // rather than the merged core. For a target providing a strict subset or a
+  // name-decorated variant of a library. LibraryName stays the
+  // isLibraryAvailable() key.
+  bit Isolated = false;
 }
 
 // Reference a shared LibcallLibrary from a SystemRuntimeLibrary, dropping the
diff --git a/llvm/test/TableGen/RuntimeLibcallEmitter-library-isolated.td 
b/llvm/test/TableGen/RuntimeLibcallEmitter-library-isolated.td
new file mode 100644
index 000000000000000..47b3e13eabf5bc8
--- /dev/null
+++ b/llvm/test/TableGen/RuntimeLibcallEmitter-library-isolated.td
@@ -0,0 +1,74 @@
+// RUN: llvm-tblgen -gen-runtime-libcalls -I %p/../../include %s | FileCheck %s
+
+// An isolated LibcallLibrary shares a linker name with a merged library but is
+// emitted as its own setAvailableLibFuncs_<defname> containing only its 
members.
+// Its consumer dispatches to that function, still guarding on the real
+// LibraryName.
+
+include "llvm/IR/RuntimeLibcallsImpl.td"
+
+def MEMCPY : RuntimeLibcall;
+def MEMSET : RuntimeLibcall;
+def MALLOC : RuntimeLibcall;
+
+def memcpy : RuntimeLibcallImpl<MEMCPY>;
+def memset : RuntimeLibcallImpl<MEMSET>;
+def malloc : RuntimeLibcallImpl<MALLOC>;
+
+def IsX86 : LibcallPredicate<[{TT.getArch() == Triple::x86_64}]>;
+def isX86 : RuntimeLibcallAvailability<(all_of IsX86)>;
+def IsSPIRV : LibcallPredicate<[{TT.isSPIRV()}]>;
+def isSPIRV : RuntimeLibcallAvailability<(all_of IsSPIRV)>;
+
+// The generic libc: the full core, always available.
+def Libc : LibcallLibrary<"libc", (add memcpy, memset, malloc)>;
+
+// SPIRV supplies only memset/memcpy from libc; isolated so "libc" does not 
pull
+// in the full merged core (malloc).
+def SPIRVLibc : LibcallLibrary<"libc", (add memcpy, memset), isSPIRV> {
+  let Isolated = true;
+}
+
+def X86SystemLibrary : SystemRuntimeLibrary<isX86, (add Libc)>;
+def SPIRVSystemLibrary : SystemRuntimeLibrary<isSPIRV, (add SPIRVLibc)>;
+
+// The merged libc function carries the full core.
+
+// CHECK: void 
llvm::RTLIB::RuntimeLibcallsInfo::setAvailableLibFuncs_libc(const llvm::Triple 
&TT, ExceptionHandling ExceptionModel, FloatABI::ABIType FloatABI, StringRef 
ABIName, LongDoubleFormat LongDoubleFormat) {
+// CHECK-NEXT:   {
+// CHECK-NEXT:     static const RTLIB::LibcallImpl LibraryCalls[] = {
+// CHECK-NEXT:         RTLIB::impl_malloc, // malloc
+// 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:       setAvailable(Impl);
+// CHECK-NEXT:     }
+// CHECK-EMPTY:
+// CHECK-NEXT:   }
+// CHECK-NEXT: }
+
+// The isolated SPIRV libc is its own function with only memset/memcpy, no 
malloc.
+
+// CHECK: void 
llvm::RTLIB::RuntimeLibcallsInfo::setAvailableLibFuncs_SPIRVLibc(const 
llvm::Triple &TT, ExceptionHandling ExceptionModel, FloatABI::ABIType FloatABI, 
StringRef ABIName, LongDoubleFormat LongDoubleFormat) {
+// CHECK-NEXT:   if (TT.isSPIRV()) {
+// 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:       setAvailable(Impl);
+// CHECK-NEXT:     }
+// CHECK-EMPTY:
+// CHECK-NEXT:   }
+// CHECK-NEXT: }
+
+// The SPIRV target dispatches to the isolated function, but the availability
+// guard still uses the real "libc" linker name.
+
+// CHECK-LABEL: void 
llvm::RTLIB::RuntimeLibcallsInfo::setTargetRuntimeLibcallSets(
+// CHECK: if (TT.isSPIRV()) {
+// CHECK: if (isLibraryAvailable("libc"))
+// CHECK-NEXT: setAvailableLibFuncs_SPIRVLibc(TT, ExceptionModel, FloatABI, 
ABIName, LongDoubleFormat);
diff --git a/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp 
b/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
index 980d474ed6ee6da..6dfe4b9b0ac50c1 100644
--- a/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
@@ -720,11 +720,18 @@ void RuntimeLibcallEmitter::emitLibraryFunction(
   OS << "}\n\n";
 }
 
+// The setAvailableLibFuncs_ suffix (and merge key) for a library: its shared
+// LibraryName normally, or its own def name if isolated (so it does not 
merge).
+static StringRef libFuncKey(const Record *Lib) {
+  return Lib->getValueAsBit("Isolated") ? Lib->getName()
+                                        : Lib->getValueAsString("LibraryName");
+}
+
 MapVector<StringRef, std::vector<const Record *>>
 RuntimeLibcallEmitter::collectLibrariesByName() const {
   MapVector<StringRef, std::vector<const Record *>> LibsByName;
   for (const Record *Lib : Records.getAllDerivedDefinitions("LibcallLibrary"))
-    LibsByName[Lib->getValueAsString("LibraryName")].push_back(Lib);
+    LibsByName[libFuncKey(Lib)].push_back(Lib);
   return LibsByName;
 }
 
@@ -842,9 +849,12 @@ void 
RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls(
     // 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.
+    // members (emitted inline below). A LibraryRef also records impls to drop.
+    // Name is the linker library (the isLibraryAvailable guard); FuncSuffix is
+    // the function suffix, which differs from Name only when isolated.
     struct DispatchLib {
       StringRef Name;
+      StringRef FuncSuffix;
       std::vector<const RuntimeLibcallImpl *> Exclude;
     };
     const DagInit *MemberDag =
@@ -857,13 +867,15 @@ void 
RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls(
       if (const auto *DI = dyn_cast<DefInit>(Arg)) {
         const Record *Def = DI->getDef();
         if (Def->isSubClassOf("LibcallLibrary")) {
-          DispatchLibs.push_back({Def->getValueAsString("LibraryName"), {}});
+          DispatchLibs.push_back(
+              {Def->getValueAsString("LibraryName"), libFuncKey(Def), {}});
           continue;
         }
 
         if (Def->isSubClassOf("LibraryRef")) {
           const Record *Lib = Def->getValueAsDef("Library");
-          DispatchLib DL{Lib->getValueAsString("LibraryName"), {}};
+          DispatchLib DL{
+              Lib->getValueAsString("LibraryName"), libFuncKey(Lib), {}};
           for (const Record *ExcludeRec :
                Def->getValueAsListOfDefs("Exclude")) {
             if (const RuntimeLibcallImpl *Impl =
@@ -961,7 +973,7 @@ void 
RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls(
     for (const DispatchLib &DL : DispatchLibs) {
       OS << indent(4) << "if (isLibraryAvailable(\"" << DL.Name << "\"))\n"
          << indent(6) << "setAvailableLibFuncs_";
-      emitLibFuncSuffix(OS, DL.Name);
+      emitLibFuncSuffix(OS, DL.FuncSuffix);
       OS << "(TT, ExceptionModel, FloatABI, ABIName, LongDoubleFormat);\n";
     }
     if (!DispatchLibs.empty())

_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to