[llvm] RuntimeLibcalls: Add bitset for available libcalls (PR #150869)

Matt Arsenault via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 4 06:07:56 PDT 2025


https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/150869

>From 592679c875e38c5ed63ae95d5d7de2de4daeb15b Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Sun, 27 Jul 2025 23:26:20 +0900
Subject: [PATCH 1/2] RuntimeLibcalls: Add bitset for available libcalls

This is a step towards separating the set of available libcalls
from the lowering decision of which call to use. Libcall recognition
now directly checks availability instead of indirectly checking through
the lowering table.
---
 llvm/include/llvm/IR/RuntimeLibcalls.h        | 64 +++++++++++++++++++
 llvm/lib/IR/RuntimeLibcalls.cpp               |  8 +--
 .../RuntimeLibcallEmitter-calling-conv.td     | 22 +++++++
 .../RuntimeLibcallEmitter-conflict-warning.td | 15 +++++
 llvm/test/TableGen/RuntimeLibcallEmitter.td   | 27 ++++++++
 .../TableGen/Basic/RuntimeLibcallsEmitter.cpp | 36 ++++++++++-
 6 files changed, 164 insertions(+), 8 deletions(-)

diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.h b/llvm/include/llvm/IR/RuntimeLibcalls.h
index f39e2e3c26900..8a5f953b68f9d 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.h
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.h
@@ -53,8 +53,64 @@ static inline auto libcall_impls() {
   return enum_seq(static_cast<RTLIB::LibcallImpl>(1), RTLIB::NumLibcallImpls);
 }
 
+/// Manage a bitset representing the list of available libcalls for a module.
+///
+/// Most of this exists because std::bitset cannot be statically constructed in
+/// a size large enough before c++23
+class LibcallImplBitset {
+private:
+  using BitWord = uint64_t;
+  static constexpr unsigned BitWordSize = sizeof(BitWord) * CHAR_BIT;
+  static constexpr size_t NumArrayElts =
+      divideCeil(RTLIB::NumLibcallImpls, BitWordSize);
+  using Storage = BitWord[NumArrayElts];
+
+  Storage Bits = {};
+
+  /// Get bitmask for \p Impl in its Bits element.
+  static constexpr BitWord getBitmask(RTLIB::LibcallImpl Impl) {
+    unsigned Idx = static_cast<unsigned>(Impl);
+    return BitWord(1) << (Idx % BitWordSize);
+  }
+
+  /// Get index of array element of Bits for \p Impl
+  static constexpr unsigned getArrayIdx(RTLIB::LibcallImpl Impl) {
+    return static_cast<unsigned>(Impl) / BitWordSize;
+  }
+
+public:
+  constexpr LibcallImplBitset() = default;
+  constexpr LibcallImplBitset(const Storage &Src) {
+    for (size_t I = 0; I != NumArrayElts; ++I)
+      Bits[I] = Src[I];
+  }
+
+  /// Check if a LibcallImpl is available.
+  constexpr bool test(RTLIB::LibcallImpl Impl) const {
+    BitWord Mask = getBitmask(Impl);
+    return (Bits[getArrayIdx(Impl)] & Mask) != 0;
+  }
+
+  /// Mark a LibcallImpl as available
+  void set(RTLIB::LibcallImpl Impl) {
+    assert(Impl != RTLIB::Unsupported && "cannot enable unsupported libcall");
+    Bits[getArrayIdx(Impl)] |= getBitmask(Impl);
+  }
+
+  /// Mark a LibcallImpl as unavailable
+  void unset(RTLIB::LibcallImpl Impl) {
+    assert(Impl != RTLIB::Unsupported && "cannot enable unsupported libcall");
+    Bits[getArrayIdx(Impl)] &= ~getBitmask(Impl);
+  }
+};
+
 /// A simple container for information about the supported runtime calls.
 struct RuntimeLibcallsInfo {
+private:
+  /// Bitset of libcalls a module may emit a call to.
+  LibcallImplBitset AvailableLibcallImpls;
+
+public:
   explicit RuntimeLibcallsInfo(
       const Triple &TT,
       ExceptionHandling ExceptionModel = ExceptionHandling::None,
@@ -132,6 +188,14 @@ struct RuntimeLibcallsInfo {
     return ImplToLibcall[Impl];
   }
 
+  bool isAvailable(RTLIB::LibcallImpl Impl) const {
+    return AvailableLibcallImpls.test(Impl);
+  }
+
+  void setAvailable(RTLIB::LibcallImpl Impl) {
+    AvailableLibcallImpls.set(Impl);
+  }
+
   /// Check if this is valid libcall for the current module, otherwise
   /// RTLIB::Unsupported.
   LLVM_ABI RTLIB::LibcallImpl getSupportedLibcallImpl(StringRef FuncName) const;
diff --git a/llvm/lib/IR/RuntimeLibcalls.cpp b/llvm/lib/IR/RuntimeLibcalls.cpp
index b6e2cac5f7b04..07c71392ee628 100644
--- a/llvm/lib/IR/RuntimeLibcalls.cpp
+++ b/llvm/lib/IR/RuntimeLibcalls.cpp
@@ -98,12 +98,8 @@ RuntimeLibcallsInfo::getSupportedLibcallImpl(StringRef FuncName) const {
   for (auto I = Range.begin(); I != Range.end(); ++I) {
     RTLIB::LibcallImpl Impl =
         static_cast<RTLIB::LibcallImpl>(I - RuntimeLibcallNameOffsets.begin());
-
-    // FIXME: This should not depend on looking up ImplToLibcall, only the list
-    // of libcalls for the module.
-    RTLIB::LibcallImpl Recognized = LibcallImpls[ImplToLibcall[Impl]];
-    if (Recognized != RTLIB::Unsupported)
-      return Recognized;
+    if (isAvailable(Impl))
+      return Impl;
   }
 
   return RTLIB::Unsupported;
diff --git a/llvm/test/TableGen/RuntimeLibcallEmitter-calling-conv.td b/llvm/test/TableGen/RuntimeLibcallEmitter-calling-conv.td
index 49d5ecaa0e5c5..14c811da64910 100644
--- a/llvm/test/TableGen/RuntimeLibcallEmitter-calling-conv.td
+++ b/llvm/test/TableGen/RuntimeLibcallEmitter-calling-conv.td
@@ -48,12 +48,18 @@ def MSP430LibraryWithCondCC : SystemRuntimeLibrary<isMSP430,
 // CHECK-NEXT:     Entry = DefaultCC;
 // CHECK-NEXT:   }
 // CHECK-EMPTY:
+// CHECK-NEXT:   static constexpr LibcallImplBitset SystemAvailableImpls({
+// CHECK-NEXT:     0x0000000000001a
+// CHECK-NEXT:   });
+// CHECK-NEXT:   AvailableLibcallImpls = SystemAvailableImpls;
+// CHECK-EMPTY:
 // CHECK-NEXT:    static const LibcallImplPair LibraryCalls[] = {
 // CHECK-NEXT:        {RTLIB::MALLOC, RTLIB::malloc}, // malloc
 // CHECK-NEXT:    };
 // CHECK-EMPTY:
 // CHECK-NEXT:    for (const auto [Func, Impl] : LibraryCalls) {
 // CHECK-NEXT:      setLibcallImpl(Func, Impl);
+// CHECK-NEXT:      setAvailable(Impl);
 // CHECK-NEXT:    }
 // CHECK-EMPTY:
 // CHECK-NEXT:    static const LibcallImplPair LibraryCalls_AlwaysAvailable[] = {
@@ -63,6 +69,7 @@ def MSP430LibraryWithCondCC : SystemRuntimeLibrary<isMSP430,
 // CHECK-EMPTY:
 // CHECK-NEXT:    for (const auto [Func, Impl] : LibraryCalls_AlwaysAvailable) {
 // CHECK-NEXT:      setLibcallImpl(Func, Impl);
+// CHECK-NEXT:      setAvailable(Impl);
 // CHECK-NEXT:      setLibcallImplCallingConv(Impl, CallingConv::AVR_BUILTIN);
 // CHECK-NEXT:    }
 // CHECK-EMPTY:
@@ -70,12 +77,18 @@ def MSP430LibraryWithCondCC : SystemRuntimeLibrary<isMSP430,
 // CHECK-NEXT:  }
 // CHECK-EMPTY:
 // CHECK-NEXT: if (TT.getArch() == Triple::avr) {
+// CHECK-NEXT:   static constexpr LibcallImplBitset SystemAvailableImpls({
+// CHECK-NEXT:     0x0000000000001a
+// CHECK-NEXT:   });
+// CHECK-NEXT:   AvailableLibcallImpls = SystemAvailableImpls;
+// CHECK-EMPTY:
 // CHECK-NEXT:   static const LibcallImplPair LibraryCalls[] = {
 // CHECK-NEXT:       {RTLIB::MALLOC, RTLIB::malloc}, // malloc
 // CHECK-NEXT:   };
 // CHECK-EMPTY:
 // CHECK-NEXT:   for (const auto [Func, Impl] : LibraryCalls) {
 // CHECK-NEXT:     setLibcallImpl(Func, Impl);
+// CHECK-NEXT:     setAvailable(Impl);
 // CHECK-NEXT:   }
 // CHECK-EMPTY:
 // CHECK-NEXT:   static const LibcallImplPair LibraryCalls_AlwaysAvailable[] = {
@@ -85,6 +98,7 @@ def MSP430LibraryWithCondCC : SystemRuntimeLibrary<isMSP430,
 // CHECK-EMPTY:
 // CHECK-NEXT:   for (const auto [Func, Impl] : LibraryCalls_AlwaysAvailable) {
 // CHECK-NEXT:     setLibcallImpl(Func, Impl);
+// CHECK-NEXT:     setAvailable(Impl);
 // CHECK-NEXT:     setLibcallImplCallingConv(Impl, CallingConv::AVR_BUILTIN);
 // CHECK-NEXT:   }
 // CHECK-EMPTY:
@@ -92,12 +106,18 @@ def MSP430LibraryWithCondCC : SystemRuntimeLibrary<isMSP430,
 // CHECK-NEXT:  }
 // CHECK-EMPTY:
 // CHECK-NEXT:  if (TT.getArch() == Triple::msp430) {
+// CHECK-NEXT:    static constexpr LibcallImplBitset SystemAvailableImpls({
+// CHECK-NEXT:      0x00000000000010
+// CHECK-NEXT:    });
+// CHECK-NEXT:    AvailableLibcallImpls = SystemAvailableImpls;
+// CHECK-EMPTY:
 // CHECK-NEXT:    static const LibcallImplPair LibraryCalls[] = {
 // CHECK-NEXT:        {RTLIB::MALLOC, RTLIB::malloc}, // malloc
 // CHECK-NEXT:    };
 // CHECK-EMPTY:
 // CHECK-NEXT:    for (const auto [Func, Impl] : LibraryCalls) {
 // CHECK-NEXT:      setLibcallImpl(Func, Impl);
+// CHECK-NEXT:      setAvailable(Impl);
 // CHECK-NEXT:    }
 // CHECK-EMPTY:
 // CHECK-NEXT:    if ( isFoo() ) {
@@ -107,6 +127,7 @@ def MSP430LibraryWithCondCC : SystemRuntimeLibrary<isMSP430,
 // CHECK-EMPTY:
 // CHECK-NEXT:      for (const auto [Func, Impl] : LibraryCalls_anonymous_3) {
 // CHECK-NEXT:        setLibcallImpl(Func, Impl);
+// CHECK-NEXT:        setAvailable(Impl);
 // CHECK-NEXT:        setLibcallImplCallingConv(Impl, CallingConv::AVR_BUILTIN);
 // CHECK-NEXT:      }
 // CHECK-EMPTY:
@@ -119,6 +140,7 @@ def MSP430LibraryWithCondCC : SystemRuntimeLibrary<isMSP430,
 // CHECK-EMPTY:
 // CHECK-NEXT:      for (const auto [Func, Impl] : LibraryCalls_anonymous_5) {
 // CHECK-NEXT:        setLibcallImpl(Func, Impl);
+// CHECK-NEXT:        setAvailable(Impl);
 // CHECK-NEXT:        setLibcallImplCallingConv(Impl, CallingConv::MSP430_BUILTIN);
 // CHECK-NEXT:      }
 // CHECK-EMPTY:
diff --git a/llvm/test/TableGen/RuntimeLibcallEmitter-conflict-warning.td b/llvm/test/TableGen/RuntimeLibcallEmitter-conflict-warning.td
index 00c9d53846326..2a8739335a8dd 100644
--- a/llvm/test/TableGen/RuntimeLibcallEmitter-conflict-warning.td
+++ b/llvm/test/TableGen/RuntimeLibcallEmitter-conflict-warning.td
@@ -25,6 +25,11 @@ def dup1 : RuntimeLibcallImpl<ANOTHER_DUP>;
 // func_a and func_b both provide SOME_FUNC.
 
 // CHECK: if (isTargetArchA()) {
+// CHECK-NEXT: static constexpr LibcallImplBitset SystemAvailableImpls({
+// CHECK-NEXT:   0x00000000000018
+// CHECK-NEXT: });
+// CHECK-NEXT: AvailableLibcallImpls = SystemAvailableImpls;
+// CHECK-EMPTY:
 // CHECK-NEXT: static const LibcallImplPair LibraryCalls[] = {
 // CHECK-NEXT:   {RTLIB::SOME_FUNC, RTLIB::func_b}, // func_b
 // CHECK-NEXT: };
@@ -35,6 +40,11 @@ def TheSystemLibraryA : SystemRuntimeLibrary<isTargetArchA,
 >;
 
 // CHECK: if (isTargetArchB()) {
+// CHECK-NEXT: static constexpr LibcallImplBitset SystemAvailableImpls({
+// CHECK-NEXT:   0x00000000000058
+// CHECK-NEXT: });
+// CHECK-NEXT: AvailableLibcallImpls = SystemAvailableImpls;
+// CHECK-EMPTY:
 // CHECK-NEXT: static const LibcallImplPair LibraryCalls[] = {
 // CHECK-NEXT:   {RTLIB::OTHER_FUNC, RTLIB::other_func}, // other_func
 // CHECK-NEXT:  {RTLIB::SOME_FUNC, RTLIB::func_a}, // func_a
@@ -46,6 +56,11 @@ def TheSystemLibraryB : SystemRuntimeLibrary<isTargetArchB,
 >;
 
 // CHECK: if (isTargetArchC()) {
+// CHECK-NEXT: static constexpr LibcallImplBitset SystemAvailableImpls({
+// CHECK-NEXT:   0x0000000000007e
+// CHECK-NEXT: });
+// CHECK-NEXT: AvailableLibcallImpls = SystemAvailableImpls;
+// CHECK-EMPTY:
 // CHECK-NEXT: static const LibcallImplPair LibraryCalls[] = {
 // CHECK-NEXT:   {RTLIB::ANOTHER_DUP, RTLIB::dup1}, // dup1
 // CHECK-NEXT:   {RTLIB::OTHER_FUNC, RTLIB::other_func}, // other_func
diff --git a/llvm/test/TableGen/RuntimeLibcallEmitter.td b/llvm/test/TableGen/RuntimeLibcallEmitter.td
index 642f8b85a89c6..97a632e8e75c7 100644
--- a/llvm/test/TableGen/RuntimeLibcallEmitter.td
+++ b/llvm/test/TableGen/RuntimeLibcallEmitter.td
@@ -157,6 +157,11 @@ def BlahLibrary : SystemRuntimeLibrary<isBlahArch, (add calloc, LibraryWithCondi
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
 // CHECK-NEXT: if (TT.getArch() == Triple::blah) {
+// CHECK-NEXT:     static constexpr LibcallImplBitset SystemAvailableImpls({
+// CHECK-NEXT:       0x000000000000e0
+// CHECK-NEXT:     });
+// CHECK-NEXT:     AvailableLibcallImpls = SystemAvailableImpls;
+// CHECK-EMPTY:
 // CHECK-NEXT:     static const LibcallImplPair LibraryCalls[] = {
 // CHECK-NEXT:         {RTLIB::BZERO, RTLIB::bzero}, // bzero
 // CHECK-NEXT:         {RTLIB::CALLOC, RTLIB::calloc}, // calloc
@@ -165,6 +170,7 @@ def BlahLibrary : SystemRuntimeLibrary<isBlahArch, (add calloc, LibraryWithCondi
 // CHECK-EMPTY:
 // CHECK-NEXT:     for (const auto [Func, Impl] : LibraryCalls) {
 // CHECK-NEXT:       setLibcallImpl(Func, Impl);
+// CHECK-NEXT:       setAvailable(Impl);
 // CHECK-NEXT:     }
 // CHECK-EMPTY:
 // CHECK-NEXT:    if (TT.hasCompilerRT()) {
@@ -175,6 +181,7 @@ def BlahLibrary : SystemRuntimeLibrary<isBlahArch, (add calloc, LibraryWithCondi
 // CHECK-EMPTY:
 // CHECK-NEXT:      for (const auto [Func, Impl] : LibraryCalls_hasCompilerRT) {
 // CHECK-NEXT:        setLibcallImpl(Func, Impl);
+// CHECK-NEXT:        setAvailable(Impl);
 // CHECK-NEXT:      }
 // CHECK-EMPTY:
 // CHECK-NEXT:    }
@@ -186,6 +193,7 @@ def BlahLibrary : SystemRuntimeLibrary<isBlahArch, (add calloc, LibraryWithCondi
 // CHECK-EMPTY:
 // CHECK-NEXT:      for (const auto [Func, Impl] : LibraryCalls_isBarOS) {
 // CHECK-NEXT:        setLibcallImpl(Func, Impl);
+// CHECK-NEXT:        setAvailable(Impl);
 // CHECK-NEXT:      }
 // CHECK-EMPTY:
 // CHECK-NEXT:    }
@@ -194,6 +202,11 @@ def BlahLibrary : SystemRuntimeLibrary<isBlahArch, (add calloc, LibraryWithCondi
 // CHECK-NEXT: }
 // CHECK-EMPTY:
 // CHECK-NEXT: if (TT.getArch() == Triple::buzz) {
+// CHECK-NEXT:    static constexpr LibcallImplBitset SystemAvailableImpls({
+// CHECK-NEXT:      0x00000000000118
+// CHECK-NEXT:    });
+// CHECK-NEXT:    AvailableLibcallImpls = SystemAvailableImpls;
+// CHECK-EMPTY:
 // CHECK-NEXT:    static const LibcallImplPair LibraryCalls[] = {
 // CHECK-NEXT:        {RTLIB::SHL_I32, RTLIB::__ashlsi3}, // __ashlsi3
 // CHECK-NEXT:        {RTLIB::SQRT_F80, RTLIB::sqrtl_f80}, // sqrtl
@@ -202,12 +215,18 @@ def BlahLibrary : SystemRuntimeLibrary<isBlahArch, (add calloc, LibraryWithCondi
 // CHECK-EMPTY:
 // CHECK-NEXT:    for (const auto [Func, Impl] : LibraryCalls) {
 // CHECK-NEXT:      setLibcallImpl(Func, Impl);
+// CHECK-NEXT:      setAvailable(Impl);
 // CHECK-NEXT:    }
 // CHECK-EMPTY:
 // CHECK-NEXT:   return;
 // CHECK-NEXT: }
 // CHECK-EMPTY:
 // CHECK-NEXT: if (TT.getArch() == Triple::foo) {
+// CHECK-NEXT:    static constexpr LibcallImplBitset SystemAvailableImpls({
+// CHECK-NEXT:      0x000000000000a0
+// CHECK-NEXT:    });
+// CHECK-NEXT:    AvailableLibcallImpls = SystemAvailableImpls;
+// CHECK-EMPTY:
 // CHECK-NEXT:    static const LibcallImplPair LibraryCalls[] = {
 // CHECK-NEXT:        {RTLIB::BZERO, RTLIB::bzero}, // bzero
 // CHECK-NEXT:        {RTLIB::SQRT_F128, RTLIB::sqrtl_f128}, // sqrtl
@@ -215,6 +234,7 @@ def BlahLibrary : SystemRuntimeLibrary<isBlahArch, (add calloc, LibraryWithCondi
 // CHECK-EMPTY:
 // CHECK-NEXT:    for (const auto [Func, Impl] : LibraryCalls) {
 // CHECK-NEXT:      setLibcallImpl(Func, Impl);
+// CHECK-NEXT:      setAvailable(Impl);
 // CHECK-NEXT:    }
 // CHECK-EMPTY:
 // CHECK-NEXT:    if (TT.getOS() == Triple::bar) {
@@ -224,6 +244,7 @@ def BlahLibrary : SystemRuntimeLibrary<isBlahArch, (add calloc, LibraryWithCondi
 // CHECK-EMPTY:
 // CHECK-NEXT:      for (const auto [Func, Impl] : LibraryCalls_isBarOS) {
 // CHECK-NEXT:        setLibcallImpl(Func, Impl);
+// CHECK-NEXT:        setAvailable(Impl);
 // CHECK-NEXT:      }
 // CHECK-EMPTY:
 // CHECK-NEXT:    }
@@ -232,6 +253,11 @@ def BlahLibrary : SystemRuntimeLibrary<isBlahArch, (add calloc, LibraryWithCondi
 // CHECK-NEXT:  }
 // CHECK-EMPTY:
 // CHECK-NEXT: if (TT.getArch() == Triple::simple) {
+// CHECK-NEXT:    static constexpr LibcallImplBitset SystemAvailableImpls({
+// CHECK-NEXT:      0x00000000000158
+// CHECK-NEXT:    });
+// CHECK-NEXT:    AvailableLibcallImpls = SystemAvailableImpls;
+// CHECK-EMPTY:
 // CHECK-NEXT:    static const LibcallImplPair LibraryCalls[] = {
 // CHECK-NEXT:        {RTLIB::CALLOC, RTLIB::calloc}, // calloc
 // CHECK-NEXT:        {RTLIB::SHL_I32, RTLIB::__ashlsi3}, // __ashlsi3
@@ -241,6 +267,7 @@ def BlahLibrary : SystemRuntimeLibrary<isBlahArch, (add calloc, LibraryWithCondi
 // CHECK-EMPTY:
 // CHECK-NEXT:    for (const auto [Func, Impl] : LibraryCalls) {
 // CHECK-NEXT:      setLibcallImpl(Func, Impl);
+// CHECK-NEXT:      setAvailable(Impl);
 // CHECK-NEXT:    }
 // CHECK-EMPTY:
 // CHECK-NEXT:   return;
diff --git a/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp b/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
index 412431b96d030..de361161a8bfd 100644
--- a/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
@@ -8,6 +8,7 @@
 
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Debug.h"
+#include "llvm/Support/Format.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/TableGen/Error.h"
@@ -402,6 +403,10 @@ void RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls(
         PredicateWithCC()); // No predicate or CC override first.
 
     DenseMap<PredicateWithCC, LibcallsWithCC> Pred2Funcs;
+
+    SmallVector<uint64_t, 32> BitsetValues(
+        divideCeil(RuntimeLibcallImplDefList.size(), 64));
+
     for (const Record *Elt : *Elements) {
       const RuntimeLibcallImpl *LibCallImpl = getRuntimeLibcallImpl(Elt);
       if (!LibCallImpl) {
@@ -410,16 +415,24 @@ void RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls(
         continue;
       }
 
+      size_t BitIdx = LibCallImpl->getEnumVal();
+      uint64_t BitmaskVal = uint64_t(1) << (BitIdx % 64);
+      size_t BitsetIdx = BitIdx / 64;
+
       auto It = Func2Preds.find(LibCallImpl);
       if (It == Func2Preds.end()) {
+        BitsetValues[BitsetIdx] |= BitmaskVal;
         Pred2Funcs[PredicateWithCC()].LibcallImpls.push_back(LibCallImpl);
         continue;
       }
 
       for (const Record *Pred : It->second.first) {
         const Record *CC = It->second.second;
-        PredicateWithCC Key(Pred, CC);
+        AvailabilityPredicate SubsetPredicate(Pred);
+        if (SubsetPredicate.isAlwaysAvailable())
+          BitsetValues[BitsetIdx] |= BitmaskVal;
 
+        PredicateWithCC Key(Pred, CC);
         auto &Entry = Pred2Funcs[Key];
         Entry.LibcallImpls.push_back(LibCallImpl);
         Entry.CallingConv = It->second.second;
@@ -427,6 +440,22 @@ void RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls(
       }
     }
 
+    OS << "    static constexpr LibcallImplBitset SystemAvailableImpls({\n"
+       << indent(6);
+
+    ListSeparator LS;
+    unsigned EntryCount = 0;
+    for (uint64_t Bits : BitsetValues) {
+      if (EntryCount++ == 4) {
+        EntryCount = 1;
+        OS << ",\n" << indent(6);
+      } else
+        OS << LS;
+      OS << format_hex(Bits, 16);
+    }
+    OS << "\n    });\n"
+          "    AvailableLibcallImpls = SystemAvailableImpls;\n\n";
+
     SmallVector<PredicateWithCC, 0> SortedPredicates =
         PredicateSorter.takeVector();
 
@@ -498,8 +527,11 @@ void RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls(
          << indent(IndentDepth + 2)
          << "for (const auto [Func, Impl] : LibraryCalls";
       SubsetPredicate.emitTableVariableNameSuffix(OS);
+
+      // TODO: setAvailable should be emitted without the uniquing above
       OS << ") {\n"
-         << indent(IndentDepth + 4) << "setLibcallImpl(Func, Impl);\n";
+         << indent(IndentDepth + 4) << "setLibcallImpl(Func, Impl);\n"
+         << indent(IndentDepth + 4) << "setAvailable(Impl);\n";
 
       if (FuncsWithCC.CallingConv) {
         StringRef CCEnum =

>From 7b13ce5bd2ecff9202cee3a58b96717d7bbfbc37 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Mon, 4 Aug 2025 21:25:49 +0900
Subject: [PATCH 2/2] Use Bitset

---
 llvm/include/llvm/ADT/Bitset.h                | 13 +++--
 llvm/include/llvm/IR/RuntimeLibcalls.h        | 51 ++-----------------
 .../TableGen/Basic/RuntimeLibcallsEmitter.cpp | 10 ++--
 3 files changed, 20 insertions(+), 54 deletions(-)

diff --git a/llvm/include/llvm/ADT/Bitset.h b/llvm/include/llvm/ADT/Bitset.h
index 55b96e47f2ed6..ecb6b149f0494 100644
--- a/llvm/include/llvm/ADT/Bitset.h
+++ b/llvm/include/llvm/ADT/Bitset.h
@@ -35,12 +35,17 @@ class Bitset {
   static_assert(BITWORD_SIZE == 64 || BITWORD_SIZE == 32,
                 "Unsupported word size");
 
-  static constexpr unsigned NumWords = (NumBits + BITWORD_SIZE-1) / BITWORD_SIZE;
-  std::array<BitWord, NumWords> Bits{};
+  static constexpr unsigned NumWords =
+      (NumBits + BITWORD_SIZE - 1) / BITWORD_SIZE;
 
 protected:
-  constexpr Bitset(const std::array<BitWord, NumWords> &B)
-      : Bits{B} {}
+  using StorageType = std::array<BitWord, NumWords>;
+
+private:
+  StorageType Bits{};
+
+protected:
+  constexpr Bitset(const StorageType &B) : Bits{B} {}
 
 public:
   constexpr Bitset() = default;
diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.h b/llvm/include/llvm/IR/RuntimeLibcalls.h
index 8a5f953b68f9d..04bda69c40280 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.h
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.h
@@ -15,6 +15,7 @@
 #define LLVM_IR_RUNTIME_LIBCALLS_H
 
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Bitset.h"
 #include "llvm/ADT/Sequence.h"
 #include "llvm/ADT/StringTable.h"
 #include "llvm/IR/CallingConv.h"
@@ -54,54 +55,12 @@ static inline auto libcall_impls() {
 }
 
 /// Manage a bitset representing the list of available libcalls for a module.
-///
-/// Most of this exists because std::bitset cannot be statically constructed in
-/// a size large enough before c++23
-class LibcallImplBitset {
-private:
-  using BitWord = uint64_t;
-  static constexpr unsigned BitWordSize = sizeof(BitWord) * CHAR_BIT;
-  static constexpr size_t NumArrayElts =
-      divideCeil(RTLIB::NumLibcallImpls, BitWordSize);
-  using Storage = BitWord[NumArrayElts];
-
-  Storage Bits = {};
-
-  /// Get bitmask for \p Impl in its Bits element.
-  static constexpr BitWord getBitmask(RTLIB::LibcallImpl Impl) {
-    unsigned Idx = static_cast<unsigned>(Impl);
-    return BitWord(1) << (Idx % BitWordSize);
-  }
-
-  /// Get index of array element of Bits for \p Impl
-  static constexpr unsigned getArrayIdx(RTLIB::LibcallImpl Impl) {
-    return static_cast<unsigned>(Impl) / BitWordSize;
-  }
-
+class LibcallImplBitset : public Bitset<RTLIB::NumLibcallImpls> {
 public:
   constexpr LibcallImplBitset() = default;
-  constexpr LibcallImplBitset(const Storage &Src) {
-    for (size_t I = 0; I != NumArrayElts; ++I)
-      Bits[I] = Src[I];
-  }
-
-  /// Check if a LibcallImpl is available.
-  constexpr bool test(RTLIB::LibcallImpl Impl) const {
-    BitWord Mask = getBitmask(Impl);
-    return (Bits[getArrayIdx(Impl)] & Mask) != 0;
-  }
-
-  /// Mark a LibcallImpl as available
-  void set(RTLIB::LibcallImpl Impl) {
-    assert(Impl != RTLIB::Unsupported && "cannot enable unsupported libcall");
-    Bits[getArrayIdx(Impl)] |= getBitmask(Impl);
-  }
-
-  /// Mark a LibcallImpl as unavailable
-  void unset(RTLIB::LibcallImpl Impl) {
-    assert(Impl != RTLIB::Unsupported && "cannot enable unsupported libcall");
-    Bits[getArrayIdx(Impl)] &= ~getBitmask(Impl);
-  }
+  constexpr LibcallImplBitset(
+      const Bitset<RTLIB::NumLibcallImpls>::StorageType &Src)
+      : Bitset(Src) {}
 };
 
 /// A simple container for information about the supported runtime calls.
diff --git a/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp b/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
index de361161a8bfd..72082c8d1611e 100644
--- a/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
@@ -402,10 +402,12 @@ void RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls(
     PredicateSorter.insert(
         PredicateWithCC()); // No predicate or CC override first.
 
+    constexpr unsigned BitsPerStorageElt = sizeof(uintptr_t) * CHAR_BIT;
+
     DenseMap<PredicateWithCC, LibcallsWithCC> Pred2Funcs;
 
-    SmallVector<uint64_t, 32> BitsetValues(
-        divideCeil(RuntimeLibcallImplDefList.size(), 64));
+    SmallVector<uintptr_t, 32> BitsetValues(
+        divideCeil(RuntimeLibcallImplDefList.size(), BitsPerStorageElt));
 
     for (const Record *Elt : *Elements) {
       const RuntimeLibcallImpl *LibCallImpl = getRuntimeLibcallImpl(Elt);
@@ -416,8 +418,8 @@ void RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls(
       }
 
       size_t BitIdx = LibCallImpl->getEnumVal();
-      uint64_t BitmaskVal = uint64_t(1) << (BitIdx % 64);
-      size_t BitsetIdx = BitIdx / 64;
+      uintptr_t BitmaskVal = uintptr_t(1) << (BitIdx % BitsPerStorageElt);
+      size_t BitsetIdx = BitIdx / BitsPerStorageElt;
 
       auto It = Func2Preds.find(LibCallImpl);
       if (It == Func2Preds.end()) {



More information about the llvm-commits mailing list