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


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-ir

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