[llvm-branch-commits] [llvm] RuntimeLibcallsEmitter: Add isolated LibcallLibrary variant (PR #223764)
Matt Arsenault via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Oct 6 13:50:44 PDT 2026
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 <Matthew.Arsenault at amd.com>
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) <noreply at anthropic.com>
---
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())
More information about the llvm-branch-commits
mailing list