[llvm-branch-commits] [llvm] RuntimeLibcalls: Add LibraryRef for dispatch-with-exclusion (PR #218869)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Aug 26 02:36:21 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-tablegen
Author: Matt Arsenault (arsenm)
<details>
<summary>Changes</summary>
Let a SystemRuntimeLibrary dispatch a shared provider library while dropping the
impls the target replaces, since a library reference cannot nest inside (sub ...).
This is a compromise from the ideal of explicitly listing all calls, but getting
to that point is prooving to be difficult.
The opt-out is emitted inside setAvailableLibFuncs_<lib>, so the single library's
logic is self contained.
Co-authored-by: Claude (Opus 4.8) <noreply@<!-- -->anthropic.com>
---
Full diff: https://github.com/llvm/llvm-project/pull/218869.diff
4 Files Affected:
- (modified) llvm/include/llvm/IR/RuntimeLibcalls.h (+4)
- (modified) llvm/include/llvm/IR/RuntimeLibcallsImpl.td (+8)
- (added) llvm/test/TableGen/RuntimeLibcallEmitter-library-ref.td (+53)
- (modified) llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp (+89-18)
``````````diff
diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.h b/llvm/include/llvm/IR/RuntimeLibcalls.h
index e7dfec449a842..43c1ffb6317b0 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.h
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.h
@@ -150,6 +150,10 @@ struct RuntimeLibcallsInfo {
AvailableLibcallImpls.set(Impl);
}
+ void setUnavailable(RTLIB::LibcallImpl Impl) {
+ AvailableLibcallImpls.reset(Impl);
+ }
+
/// Check if a function name is a recognized runtime call of any kind. This
/// does not consider if this call is available for any current compilation,
/// just that it is a known call somewhere. This returns the set of all
diff --git a/llvm/include/llvm/IR/RuntimeLibcallsImpl.td b/llvm/include/llvm/IR/RuntimeLibcallsImpl.td
index 59616efb41c57..516cf0515a103 100644
--- a/llvm/include/llvm/IR/RuntimeLibcallsImpl.td
+++ b/llvm/include/llvm/IR/RuntimeLibcallsImpl.td
@@ -125,6 +125,14 @@ class LibcallLibrary<string name, dag impls,
RuntimeLibcallAvailability Pred = pred;
}
+// Reference a shared LibcallLibrary from a SystemRuntimeLibrary, dropping the
+// impls in `exclude`. Lets an override target pull a provider library (e.g.
+// compiler-rt) but keep its own versions of a few entries.
+class LibraryRef<LibcallLibrary lib, list<RuntimeLibcallImpl> exclude> {
+ LibcallLibrary Library = lib;
+ list<RuntimeLibcallImpl> Exclude = exclude;
+}
+
/// 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/test/TableGen/RuntimeLibcallEmitter-library-ref.td b/llvm/test/TableGen/RuntimeLibcallEmitter-library-ref.td
new file mode 100644
index 0000000000000..09bfc96a4d716
--- /dev/null
+++ b/llvm/test/TableGen/RuntimeLibcallEmitter-library-ref.td
@@ -0,0 +1,53 @@
+// RUN: llvm-tblgen -gen-runtime-libcalls -I %p/../../include %s | FileCheck %s
+
+// Check that a LibraryRef<Lib, [impls]> member dispatches to the library like a
+// plain reference, with the excluded impls emitted as setUnavailable inside the
+// library function (guarded on the consumer's triple), so the dispatcher stays a
+// plain call.
+
+include "llvm/IR/RuntimeLibcallsImpl.td"
+
+def MEMCPY : RuntimeLibcall;
+def DIV_I32 : RuntimeLibcall;
+def MOD_I32 : RuntimeLibcall;
+def SQRT_F64 : RuntimeLibcall;
+
+def memcpy : RuntimeLibcallImpl<MEMCPY>;
+def __divsi3 : RuntimeLibcallImpl<DIV_I32>;
+def __modsi3 : RuntimeLibcallImpl<MOD_I32>;
+def sqrt : RuntimeLibcallImpl<SQRT_F64, "sqrt">;
+
+def IsX86 : LibcallPredicate<[{TT.isX86()}]>;
+def isX86 : RuntimeLibcallAvailability<(all_of IsX86)>;
+
+def CompilerRt : LibcallLibrary<"compiler-rt", (add memcpy, __divsi3, __modsi3)>;
+def Libm : LibcallLibrary<"libm", (add sqrt)>;
+
+// Dispatch compiler-rt but drop the two div/mod entries this target overrides;
+// dispatch libm plainly.
+def X86System : SystemRuntimeLibrary<isX86,
+ (add LibraryRef<CompilerRt, [__divsi3, __modsi3]>, Libm)>;
+
+// The compiler-rt library function sets its members available, then emits the
+// X86 consumer's opt-out under a triple guard at the end of the same function.
+// CHECK: static void setAvailableLibFuncs_compiler_rt(
+// CHECK: for (const RTLIB::LibcallImpl Impl : LibraryCalls) {
+// CHECK-NEXT: Info.setAvailable(Impl);
+// CHECK: if (TT.isX86()) {
+// CHECK-NEXT: Info.setUnavailable(RTLIB::impl___divsi3); // __divsi3
+// CHECK-NEXT: Info.setUnavailable(RTLIB::impl___modsi3); // __modsi3
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+// CHECK: static void setAvailableLibFuncs_libm(
+
+// The dispatcher is a plain call for both libraries; no setUnavailable here.
+// CHECK: void llvm::RTLIB::RuntimeLibcallsInfo::setTargetRuntimeLibcallSets(
+// CHECK: if (TT.isX86()) {
+// CHECK: AvailableLibcallImpls = SystemAvailableImpls;
+// CHECK-EMPTY:
+// CHECK-NEXT: if (isLibraryAvailable("compiler-rt"))
+// CHECK-NEXT: setAvailableLibFuncs_compiler_rt(*this, TT, ExceptionModel, FloatABI, EABIVersion, ABIName, LongDoubleFormat);
+// CHECK-NEXT: if (isLibraryAvailable("libm"))
+// CHECK-NEXT: setAvailableLibFuncs_libm(*this, TT, ExceptionModel, FloatABI, EABIVersion, ABIName, LongDoubleFormat);
+// CHECK-NOT: setUnavailable
diff --git a/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp b/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
index 1a90c36f2a879..4010619fa91c3 100644
--- a/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
@@ -98,10 +98,19 @@ class RuntimeLibcallEmitter {
SetVector<PredicateWithCC> &PredicateSorter,
unsigned BaseIndent, StringRef Receiver) const;
+ // A LibraryRef opt-out: the impls a consumer drops from a shared library,
+ // plus the consumer's triple predicate.
+ struct LibraryExclusion {
+ const Record *TriplePred;
+ std::vector<const RuntimeLibcallImpl *> Impls;
+ };
+
// Emit a file-local `setAvailableLibFuncs_<name>` for all LibcallLibrary defs
- // sharing \p Name, each gated by its own availability predicate.
+ // sharing \p Name, each gated by its own availability predicate. \p Exclusions
+ // are emitted as guarded setUnavailable calls at the end.
void emitLibraryFunction(raw_ostream &OS, StringRef Name,
- ArrayRef<const Record *> Libs) const;
+ ArrayRef<const Record *> Libs,
+ ArrayRef<LibraryExclusion> Exclusions) const;
void emitSystemRuntimeLibrarySetCalls(raw_ostream &OS) const;
@@ -487,7 +496,8 @@ static void emitLibFuncSuffix(raw_ostream &OS, StringRef Name) {
}
void RuntimeLibcallEmitter::emitLibraryFunction(
- raw_ostream &OS, StringRef Name, ArrayRef<const Record *> Libs) const {
+ raw_ostream &OS, StringRef Name, ArrayRef<const Record *> Libs,
+ ArrayRef<LibraryExclusion> Exclusions) const {
// File-local; referenced only from the driver in the same fragment.
OS << "static void setAvailableLibFuncs_";
emitLibFuncSuffix(OS, Name);
@@ -606,6 +616,21 @@ void RuntimeLibcallEmitter::emitLibraryFunction(
}
}
+ // Emit each consumer's LibraryRef opt-outs.
+ for (const LibraryExclusion &Excl : Exclusions) {
+ OS << '\n' << indent(2);
+ AvailabilityPredicate ExcludePred(Excl.TriplePred);
+ ExcludePred.emitIf(OS);
+ for (const RuntimeLibcallImpl *Impl : Excl.Impls) {
+ OS << indent(4) << "Info.setUnavailable(";
+ Impl->emitEnumEntry(OS);
+ OS << "); // " << Impl->getLibcallFuncName() << '\n';
+ }
+
+ OS << indent(2);
+ ExcludePred.emitEndIf(OS);
+ }
+
OS << "}\n\n";
}
@@ -618,12 +643,38 @@ void RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls(
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");
+ // Collect each shared library's LibraryRef opt-outs, keyed by library name,
+ // so its library function can emit them.
+ MapVector<StringRef, std::vector<LibraryExclusion>> ExclusionsByLibName;
+ for (const Record *R : AllLibs) {
+ const DagInit *MemberDag =
+ R->getValueAsDef("MemberList")->getValueAsDag("MemberList");
+ for (const Init *Arg : MemberDag->getArgs()) {
+ const auto *DI = dyn_cast<DefInit>(Arg);
+ if (!DI || !DI->getDef()->isSubClassOf("LibraryRef"))
+ continue;
+ const Record *Def = DI->getDef();
+ LibraryExclusion Excl{R->getValueAsDef("TriplePred"), {}};
+ for (const Record *ExcludeRec : Def->getValueAsListOfDefs("Exclude")) {
+ if (const RuntimeLibcallImpl *Impl =
+ Libcalls.getRuntimeLibcallImpl(ExcludeRec))
+ Excl.Impls.push_back(Impl);
+ }
+
+ if (!Excl.Impls.empty()) {
+ StringRef LibName =
+ Def->getValueAsDef("Library")->getValueAsString("LibraryName");
+ ExclusionsByLibName[LibName].push_back(std::move(Excl));
+ }
+ }
+ }
+
+ for (const auto &[Name, Libs] : LibsByName)
+ emitLibraryFunction(OS, Name, Libs, ExclusionsByLibName.lookup(Name));
+
OS << "void llvm::RTLIB::RuntimeLibcallsInfo::setTargetRuntimeLibcallSets("
"const llvm::Triple &TT, ExceptionHandling ExceptionModel, "
"FloatABI::ABIType FloatABI, EABI EABIVersion, "
@@ -650,21 +701,41 @@ void RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls(
}
}
- // Split the top-level member list into named LibcallLibrary references,
- // which are dispatched to their own setAvailableLibFuncs_<name> function
- // under an isLibraryAvailable guard, and the remaining (bare impl /
- // LibcallImpls) members, which are emitted inline via the flat path below.
+ // 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.
+ struct DispatchLib {
+ StringRef Name;
+ std::vector<const RuntimeLibcallImpl *> Exclude;
+ };
const DagInit *MemberDag =
R->getValueAsDef("MemberList")->getValueAsDag("MemberList");
- SmallVector<StringRef, 4> DispatchLibs;
+ SmallVector<DispatchLib, 4> DispatchLibs;
SmallVector<const Init *, 16> InlineArgs;
SmallVector<const StringInit *, 16> InlineArgNames;
for (auto [Arg, ArgName] :
zip_equal(MemberDag->getArgs(), MemberDag->getArgNames())) {
- if (const auto *DI = dyn_cast<DefInit>(Arg);
- DI && DI->getDef()->isSubClassOf("LibcallLibrary")) {
- DispatchLibs.push_back(DI->getDef()->getValueAsString("LibraryName"));
- continue;
+ if (const auto *DI = dyn_cast<DefInit>(Arg)) {
+ const Record *Def = DI->getDef();
+ if (Def->isSubClassOf("LibcallLibrary")) {
+ DispatchLibs.push_back({Def->getValueAsString("LibraryName"), {}});
+ continue;
+ }
+
+ if (Def->isSubClassOf("LibraryRef")) {
+ const Record *Lib = Def->getValueAsDef("Library");
+ DispatchLib DL{Lib->getValueAsString("LibraryName"), {}};
+ for (const Record *ExcludeRec :
+ Def->getValueAsListOfDefs("Exclude")) {
+ if (const RuntimeLibcallImpl *Impl =
+ Libcalls.getRuntimeLibcallImpl(ExcludeRec))
+ DL.Exclude.push_back(Impl);
+ }
+
+ DispatchLibs.push_back(std::move(DL));
+ continue;
+ }
}
InlineArgs.push_back(Arg);
InlineArgNames.push_back(ArgName);
@@ -749,10 +820,10 @@ void RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls(
// Dispatch to each named library's setup function. This must come after the
// SystemAvailableImpls assignment above (which overwrites the bitset); the
// library functions union their members in on top via setAvailable.
- for (StringRef LibName : DispatchLibs) {
- OS << indent(4) << "if (isLibraryAvailable(\"" << LibName << "\"))\n"
+ for (const DispatchLib &DL : DispatchLibs) {
+ OS << indent(4) << "if (isLibraryAvailable(\"" << DL.Name << "\"))\n"
<< indent(6) << "setAvailableLibFuncs_";
- emitLibFuncSuffix(OS, LibName);
+ emitLibFuncSuffix(OS, DL.Name);
OS << "(*this, TT, ExceptionModel, FloatABI, EABIVersion, ABIName, "
"LongDoubleFormat);\n";
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/218869
More information about the llvm-branch-commits
mailing list