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 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
