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) <[email protected]> >From cd86ae7bac49600f0f6a751cf8356994fa74db49 Mon Sep 17 00:00:00 2001 From: Matt Arsenault <[email protected]> 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) <[email protected]> --- ...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, " _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
