[llvm-branch-commits] [llvm] RuntimeLibcallsEmitter: Handle calling conv in per-library functions (PR #223760)

Matt Arsenault via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Sep 15 09:56:47 PDT 2026


https://github.com/arsenm created https://github.com/llvm/llvm-project/pull/223760

Pull handling of the default calling convention into the
setAvailableLibFuncs_<name> functions, so the library logic will be
fully contained.

Co-authored-by: Claude (Opus 4.8) <noreply at anthropic.com>

>From cd86ae7bac49600f0f6a751cf8356994fa74db49 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Thu, 13 Aug 2026 16:11:21 +0200
Subject: [PATCH] RuntimeLibcallsEmitter: Handle calling conv in per-library
 functions

Pull handling of the default calling convention into the
setAvailableLibFuncs_<name> functions, so the library logic will be
fully contained.

Co-authored-by: Claude (Opus 4.8) <noreply at anthropic.com>
---
 ...untimeLibcallEmitter-library-default-cc.td | 50 +++++++++++
 .../TableGen/Basic/RuntimeLibcallsEmitter.cpp | 85 +++++++++++++++++--
 2 files changed, 130 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/TableGen/RuntimeLibcallEmitter-library-default-cc.td

diff --git a/llvm/test/TableGen/RuntimeLibcallEmitter-library-default-cc.td b/llvm/test/TableGen/RuntimeLibcallEmitter-library-default-cc.td
new file mode 100644
index 00000000000000..349ee626c97f06
--- /dev/null
+++ b/llvm/test/TableGen/RuntimeLibcallEmitter-library-default-cc.td
@@ -0,0 +1,50 @@
+// RUN: llvm-tblgen -gen-runtime-libcalls -I %p/../../include %s | FileCheck %s
+
+// A member CC naming the DefaultCC sentinel gets a DefaultCC local
+// seeded from the consuming system library's
+// DefaultLibcallCallingConv.
+
+include "llvm/IR/RuntimeLibcallsImpl.td"
+
+def SOME_CALL : RuntimeLibcall;
+def OTHER_CALL : RuntimeLibcall;
+
+def some_impl : RuntimeLibcallImpl<SOME_CALL>;
+def other_impl : RuntimeLibcallImpl<OTHER_CALL>;
+
+def IsFoo : LibcallPredicate<[{TT.getArch() == Triple::arm}]>;
+def isFoo : RuntimeLibcallAvailability<(all_of IsFoo)>;
+
+// Predicate using the isAAPCS_ABI member helper.
+def IsAAPCS : LibcallPredicate<[{isAAPCS_ABI(TT, ABIName)}]>;
+def isAAPCS : RuntimeLibcallAvailability<(all_of IsAAPCS)>;
+
+// CC naming the DefaultCC sentinel.
+def FooDefaultOrAAPCS : LibcallCallingConv<
+  [{isAAPCS_ABI(TT, ABIName) ? static_cast<CallingConv::ID>(CallingConv::ARM_AAPCS) : static_cast<CallingConv::ID>(DefaultCC)}]
+>;
+
+// One member gated on isAAPCS_ABI, one whose CC names DefaultCC.
+def FooLib : LibcallLibrary<"foo", (add
+  LibcallImpls<(add some_impl), isAAPCS>,
+  LibcallsWithCC<(add other_impl), FooDefaultOrAAPCS>)>;
+
+def FooSystem : SystemRuntimeLibrary<isFoo, (add FooLib)> {
+  let DefaultLibcallCallingConv
+    = LibcallCallingConv<[{FloatABI == FloatABI::Hard ? CallingConv::ARM_AAPCS_VFP : CallingConv::ARM_AAPCS}]>;
+}
+
+// The library function seeds DefaultCC
+
+// CHECK: void llvm::RTLIB::RuntimeLibcallsInfo::setAvailableLibFuncs_foo(const llvm::Triple &TT, ExceptionHandling ExceptionModel, FloatABI::ABIType FloatABI, EABI EABIVersion, StringRef ABIName, LongDoubleFormat LongDoubleFormat) {
+// CHECK-NEXT:   const CallingConv::ID DefaultCC = FloatABI == FloatABI::Hard ? CallingConv::ARM_AAPCS_VFP : CallingConv::ARM_AAPCS;
+
+// CHECK: setLibcallImplCallingConv(Impl, isAAPCS_ABI(TT, ABIName) ? static_cast<CallingConv::ID>(CallingConv::ARM_AAPCS) : static_cast<CallingConv::ID>(DefaultCC));
+// CHECK: if (isAAPCS_ABI(TT, ABIName)) {
+
+// The dispatcher seeds its own whole-table DefaultCC.
+
+// CHECK: void llvm::RTLIB::RuntimeLibcallsInfo::setTargetRuntimeLibcallSets(
+// CHECK: const CallingConv::ID DefaultCC = FloatABI == FloatABI::Hard ? CallingConv::ARM_AAPCS_VFP : CallingConv::ARM_AAPCS;
+// CHECK: if (isLibraryAvailable("foo"))
+// CHECK-NEXT: setAvailableLibFuncs_foo(TT, ExceptionModel, FloatABI, EABIVersion, ABIName, LongDoubleFormat);
diff --git a/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp b/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
index 7a72b1061c737a..c44cf8b3008d36 100644
--- a/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
@@ -124,10 +124,14 @@ class RuntimeLibcallEmitter {
 
   // Emit a `setAvailableLibFuncs_<name>` member function for all LibcallLibrary
   // defs sharing \p Name, each gated by its own availability predicate. \p
-  // Exclusions are emitted as guarded setUnavailable calls at the end.
+  // Exclusions are emitted as guarded setUnavailable calls at the end. \p
+  // DefaultCCs holds the distinct DefaultLibcallCallingConv snippets the
+  // consuming system libraries supply; exactly one must exist if any member CC
+  // names the DefaultCC sentinel.
   void emitLibraryFunction(raw_ostream &OS, StringRef Name,
                            ArrayRef<const Record *> Libs,
-                           ArrayRef<LibraryExclusion> Exclusions) const;
+                           ArrayRef<LibraryExclusion> Exclusions,
+                           ArrayRef<StringRef> DefaultCCs) const;
 
   // Group all LibcallLibrary defs by their shared LibraryName, preserving
   // definition order. Both the member-declaration fragment and the definitions
@@ -544,7 +548,8 @@ void RuntimeLibcallEmitter::emitLibraryVariant(raw_ostream &OS,
 
 void RuntimeLibcallEmitter::emitLibraryFunction(
     raw_ostream &OS, StringRef Name, ArrayRef<const Record *> Libs,
-    ArrayRef<LibraryExclusion> Exclusions) const {
+    ArrayRef<LibraryExclusion> Exclusions,
+    ArrayRef<StringRef> DefaultCCs) const {
   OS << "void llvm::RTLIB::RuntimeLibcallsInfo::setAvailableLibFuncs_";
   emitLibFuncSuffix(OS, Name);
   OS << "(const llvm::Triple &TT, "
@@ -600,6 +605,37 @@ void RuntimeLibcallEmitter::emitLibraryFunction(
     Expanded.push_back(std::move(EL));
   }
 
+  // If any member CC names the DefaultCC sentinel, emit a local for it (seeded
+  // from the consuming system library's DefaultLibcallCallingConv) so those
+  // snippets are in scope. \p DefaultCCs holds the distinct snippets the
+  // consumers supply: exactly one must exist.
+  bool ReferencesDefaultCC = any_of(Expanded, [](const ExpandedLibrary &EL) {
+    return any_of(EL.Pred2Funcs, [](const auto &KeyAndFuncs) {
+      const Record *CC = KeyAndFuncs.second.CallingConv;
+      return CC && CC->getValueAsString("CallingConv").contains("DefaultCC");
+    });
+  });
+
+  if (ReferencesDefaultCC) {
+    if (DefaultCCs.empty()) {
+      PrintFatalError(Libs.front(),
+                      "library '" + Name +
+                          "' has a member calling convention referencing "
+                          "DefaultCC but no consuming SystemRuntimeLibrary "
+                          "provides a DefaultLibcallCallingConv");
+    }
+    if (DefaultCCs.size() > 1) {
+      PrintFatalError(Libs.front(),
+                      "library '" + Name +
+                          "' is dispatched by multiple SystemRuntimeLibrary "
+                          "defs with different DefaultLibcallCallingConv; "
+                          "DefaultCC is ambiguous for its member calling "
+                          "conventions");
+    }
+
+    OS << "  const CallingConv::ID DefaultCC = " << DefaultCCs.front() << ";\n";
+  }
+
   // Impls unconditional in every variant are emitted once and stripped from
   // each variant, so the shared core is not repeated.
   SetVector<const RuntimeLibcallImpl *> SharedCore;
@@ -735,8 +771,47 @@ void RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls(
     }
   }
 
-  for (const auto &[Name, Libs] : collectLibrariesByName())
-    emitLibraryFunction(OS, Name, Libs, ExclusionsByLibName.lookup(Name));
+  // Collect, per library name, the distinct DefaultLibcallCallingConv snippets
+  // its consuming system libraries supply (a plain Record walk; no member
+  // expansion). emitLibraryFunction, which already expands the members, picks
+  // the snippet for a library that names the DefaultCC sentinel and diagnoses a
+  // missing (none) or ambiguous (more than one) snippet.
+  MapVector<StringRef, SetVector<StringRef>> DefaultCCsByLibName;
+  for (const Record *R : AllLibs) {
+    const Record *DefaultCCClass =
+        R->getValueAsDef("DefaultLibcallCallingConv");
+    StringRef DefaultCC =
+        DefaultCCClass ? DefaultCCClass->getValueAsString("CallingConv").trim()
+                       : StringRef();
+    if (DefaultCC.empty())
+      continue;
+    const DagInit *MemberDag =
+        R->getValueAsDef("MemberList")->getValueAsDag("MemberList");
+    for (const Init *Arg : MemberDag->getArgs()) {
+      const auto *DI = dyn_cast<DefInit>(Arg);
+      if (!DI)
+        continue;
+      const Record *Def = DI->getDef();
+      const Record *Lib = nullptr;
+      if (Def->isSubClassOf("LibcallLibrary"))
+        Lib = Def;
+      else if (Def->isSubClassOf("LibraryRef"))
+        Lib = Def->getValueAsDef("Library");
+      if (!Lib)
+        continue;
+      DefaultCCsByLibName[Lib->getValueAsString("LibraryName")].insert(
+          DefaultCC);
+    }
+  }
+
+  for (const auto &[Name, Libs] : collectLibrariesByName()) {
+    auto It = DefaultCCsByLibName.find(Name);
+    ArrayRef<StringRef> DefaultCCs = It == DefaultCCsByLibName.end()
+                                         ? ArrayRef<StringRef>()
+                                         : It->second.getArrayRef();
+    emitLibraryFunction(OS, Name, Libs, ExclusionsByLibName.lookup(Name),
+                        DefaultCCs);
+  }
 
   OS << "void llvm::RTLIB::RuntimeLibcallsInfo::setTargetRuntimeLibcallSets("
         "const llvm::Triple &TT, ExceptionHandling ExceptionModel, "



More information about the llvm-branch-commits mailing list