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 516cf0515a1035..c8255913a9d21e 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 00000000000000..47b3e13eabf5bc --- /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 980d474ed6ee6d..6dfe4b9b0ac50c 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
