[llvm] [NFCI][TableGen] Make `Intrinsic::getAttributes` table driven (PR #152349)

Rahul Joshi via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 6 11:02:18 PDT 2025


https://github.com/jurahul created https://github.com/llvm/llvm-project/pull/152349

None

>From 6aede33b4a707c3f2705bff8cec9aead0d4f6e88 Mon Sep 17 00:00:00 2001
From: Rahul Joshi <rjoshi at nvidia.com>
Date: Wed, 6 Aug 2025 08:52:07 -0700
Subject: [PATCH] [NFCI][TableGen] Make `Intrinsic::getAttributes` table driven

---
 llvm/test/TableGen/intrinsic-attrs.td         |  18 +-
 .../utils/TableGen/Basic/IntrinsicEmitter.cpp | 176 ++++++++++++------
 2 files changed, 132 insertions(+), 62 deletions(-)

diff --git a/llvm/test/TableGen/intrinsic-attrs.td b/llvm/test/TableGen/intrinsic-attrs.td
index 92a90dcafd48c..bcded0cd2e9f1 100644
--- a/llvm/test/TableGen/intrinsic-attrs.td
+++ b/llvm/test/TableGen/intrinsic-attrs.td
@@ -27,14 +27,16 @@ def int_deref_ptr_ret : Intrinsic<[llvm_ptr_ty], [], [Dereferenceable<RetIndex,
 // CHECK: static constexpr uint16_t IntrinsicsToAttributesMap[] = {
 // CHECK: 0 << 8 | 0, // llvm.deref.ptr.ret
 // CHECK: 1 << 8 | 1, // llvm.random.gen
+// CHECK: }; // IntrinsicsToAttributesMap
+
+// CHECK: static constexpr ArgNoAttrIDPair ArgAttrIdTable[] = {
+// CHECK-NEXT:   {0, 0},
+// CHECK: }; // ArgAttrIdTable
+
+// CHECK: static constexpr ArgAttributesInfo ArgAttributesInfoTable[] = {
+// CHECK-NEXT:  {0, 1},
+// CHECK-NEXT:  {0, 0},
+// CHECK-NEXT: }; // ArgAttributesInfoTable
 
 // CHECK: getAttributes(LLVMContext &C, ID id,
 // CHECK-NEXT: FunctionType *FT) {
-// CHECK:      case 1:
-// CHECK-NEXT:   HasFnAttr = true;
-// CHECK-NEXT:   break;
-// CHECK-NEXT: case 0:
-// CHECK-NEXT:   AS[0] = {0, getIntrinsicArgAttributeSet(C, 0, FT->getContainedType(0))};
-// CHECK-NEXT:   HasFnAttr = true;
-// CHECK-NEXT:   NumAttrs = 1
-// CHECK-NEXT:   break;
diff --git a/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp b/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
index 49f719453e0bb..293e64e97cc33 100644
--- a/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
@@ -561,7 +561,8 @@ static AttributeSet getIntrinsicArgAttributeSet(LLVMContext &C, unsigned ID,
 } // getIntrinsicArgAttributeSet
 )";
 
-  // Compute unique function attribute sets.
+  // Compute unique function attribute sets. Note that ID 255 will be used for
+  // intrinsics with no function attributes.
   std::map<const CodeGenIntrinsic *, unsigned, FnAttributeComparator>
       UniqFnAttributes;
   OS << R"(
@@ -570,6 +571,8 @@ static AttributeSet getIntrinsicFnAttributeSet(LLVMContext &C, unsigned ID) {
     default: llvm_unreachable("Invalid attribute set number");)";
 
   for (const CodeGenIntrinsic &Int : Ints) {
+    if (!hasFnAttributes(Int))
+      continue;
     unsigned ID = UniqFnAttributes.size();
     if (!UniqFnAttributes.try_emplace(&Int, ID).second)
       continue;
@@ -621,9 +624,7 @@ static AttributeSet getIntrinsicFnAttributeSet(LLVMContext &C, unsigned ID) {
 
 static constexpr uint16_t IntrinsicsToAttributesMap[] = {)";
 
-  // Compute the maximum number of attribute arguments and the map. For function
-  // attributes, we only consider whether the intrinsics has any function
-  // arguments or not.
+  // Compute unique argument attributes.
   std::map<const CodeGenIntrinsic *, unsigned, AttributeComparator>
       UniqAttributes;
   for (const CodeGenIntrinsic &Int : Ints) {
@@ -631,86 +632,153 @@ static constexpr uint16_t IntrinsicsToAttributesMap[] = {)";
     UniqAttributes.try_emplace(&Int, ID);
   }
 
-  // Emit an array of AttributeList.  Most intrinsics will have at least one
-  // entry, for the function itself (index ~1), which is usually nounwind.
-  for (const CodeGenIntrinsic &Int : Ints) {
-    uint16_t FnAttrIndex = UniqFnAttributes[&Int];
-    OS << formatv("\n    {} << 8 | {}, // {}", FnAttrIndex,
-                  UniqAttributes[&Int], Int.Name);
-  }
+  constexpr uint16_t NoFunctionAttrsID = 255;
+  if (UniqAttributes.size() > 256)
+    PrintFatalError("Too many unique argument attributes for table!");
+  // Note, ID 255 is used to indicate no function attributes.
+  if (UniqFnAttributes.size() > 255)
+    PrintFatalError("Too many unique function attributes for table!");
 
   // Assign a 16-bit packed ID for each intrinsic. The lower 8-bits will be its
   // "argument attribute ID" (index in UniqAttributes) and upper 8 bits will be
   // its "function attribute ID" (index in UniqFnAttributes).
-  if (UniqAttributes.size() > 256)
-    PrintFatalError("Too many unique argument attributes for table!");
-  if (UniqFnAttributes.size() > 256)
-    PrintFatalError("Too many unique function attributes for table!");
+  for (const CodeGenIntrinsic &Int : Ints) {
+    uint16_t FnAttrIndex =
+        hasFnAttributes(Int) ? UniqFnAttributes[&Int] : NoFunctionAttrsID;
+    OS << formatv("\n    {} << 8 | {}, // {}", FnAttrIndex,
+                  UniqAttributes[&Int], Int.Name);
+  }
 
   OS << R"(
-};
+}; // IntrinsicsToAttributesMap
+)";
+
+  // For a given intrinsic, its attributes are constructed by populating the
+  // local array `AS` below with its non-empty argument attributes followed by
+  // function attributes if any. Each argument attribute is constructed as:
+  //
+  //   getIntrinsicArgAttributeSet(C, ArgAttrID, FT->getContainedType(ArgNo));
+  //
+  // Create a table that records, for each argument attributes, the set of
+  // <ArgNo, ArgAttrID> pairs that are needed to construct its argument
+  // attributes. These tables for all intrinsics will be concatenated into one
+  // large table and then for each intrinsic, we remember the Staring index and
+  // number of size of its slice of entries (i.e., number of arguments with
+  // non-empty attributes), so that we can build the attribute list for an
+  // intrinsic without using a switch-case.
+
+  // Find the max number of attributes to create the local array and create
+  // a concatenated list of <ArgNo, AttrID> pairs.
+  struct ArgNoAttrIDPair {
+    uint16_t ArgNo, ArgAttrID;
+    ArgNoAttrIDPair(uint16_t ArgNo, uint16_t ArgAttrID)
+        : ArgNo(ArgNo), ArgAttrID(ArgAttrID) {}
+  };
+
+  // For each unique ID in UniqAttributes, reacord the starting index in the
+  // flattened ArgNoAttrIDPair table, and the number of non-empty arg
+  // attributes.
+  struct ArgAttributesInfo {
+    uint16_t StartIndex;
+    uint16_t NumAttrs;
+    ArgAttributesInfo(uint16_t StartIndex, uint16_t NumAttrs)
+        : StartIndex(StartIndex), NumAttrs(NumAttrs) {}
+  };
+  SmallVector<ArgNoAttrIDPair> ArgAttrIdTable;
+  SmallVector<ArgAttributesInfo> ArgAttributesInfoTable(UniqAttributes.size(),
+                                                        {0, 0});
 
-AttributeList Intrinsic::getAttributes(LLVMContext &C, ID id,
-                                       FunctionType *FT) {)";
-  // Find the max number of attributes to create the local array.
   unsigned MaxNumAttrs = 0;
   for (const auto [IntPtr, UniqueID] : UniqAttributes) {
     const CodeGenIntrinsic &Int = *IntPtr;
-    unsigned NumAttrs =
-        llvm::count_if(Int.ArgumentAttributes,
-                       [](const auto &Attrs) { return !Attrs.empty(); });
+    unsigned NumAttrs = 0;
+    unsigned StartIndex = ArgAttrIdTable.size();
+
+    for (const auto &[ArgNo, Attrs] : enumerate(Int.ArgumentAttributes)) {
+      if (Attrs.empty())
+        continue;
+
+      uint16_t ArgAttrID = UniqArgAttributes.find(Attrs)->second;
+      ArgAttrIdTable.emplace_back((uint16_t)ArgNo, ArgAttrID);
+      ++NumAttrs;
+    }
+
+    // Record the start index and size of the list for this unique ID.
+    if (NumAttrs)
+      ArgAttributesInfoTable[UniqueID] =
+          ArgAttributesInfo(StartIndex, NumAttrs);
+
     NumAttrs += hasFnAttributes(Int);
     MaxNumAttrs = std::max(MaxNumAttrs, NumAttrs);
   }
 
+  if (ArgAttrIdTable.size() >= std::numeric_limits<uint16_t>::max())
+    PrintFatalError("Size of ArgAttrIdTable exceeds supported limit");
+
+  // Emit the 2 tables (flattened ArgNo, ArgAttrID) and ArgAttrIdTableIndex
+  OS << R"(
+namespace {
+struct ArgNoAttrIDPair {
+  uint16_t ArgNo, ArgAttrID;
+};
+} // namespace
+
+static constexpr ArgNoAttrIDPair ArgAttrIdTable[] = {
+)";
+  for (const auto &[ArgNo, ArgAttrID] : ArgAttrIdTable)
+    OS << formatv("  {{{}, {}},\n", ArgNo, ArgAttrID);
+  OS << R"(}; // ArgAttrIdTable
+
+namespace {
+struct ArgAttributesInfo {
+  uint16_t StartIndex;
+  uint16_t NumAttrs;
+};
+} // namespace
+ 
+static constexpr ArgAttributesInfo ArgAttributesInfoTable[] = {
+)";
+  for (const auto &[StartIndex, NumAttrs] : ArgAttributesInfoTable)
+    OS << formatv("  {{{}, {}},\n", StartIndex, NumAttrs);
+  OS << "}; // ArgAttributesInfoTable\n";
+
+  // Now emit the Intrinsic::getAttributes function. This will first map
+  // from intrinsic ID -> unique arg/function attr ID (using the
+  // IntrinsicsToAttributesMap) table. Then it will use the unique arg ID to
+  // construct all the argument attributes (using the ArgAttributesInfoTable and
+  // ArgAttrIdTable) and then add on the function attributes if any.
   OS << formatv(R"(
+AttributeList Intrinsic::getAttributes(LLVMContext &C, ID id,
+                                       FunctionType *FT) {{
   if (id == 0)
     return AttributeList();
 
   uint16_t PackedID = IntrinsicsToAttributesMap[id - 1];
   uint8_t FnAttrID = PackedID >> 8;
+  uint8_t ArgAttrID = PackedID & 0xFF;
   std::pair<unsigned, AttributeSet> AS[{}];
-  unsigned NumAttrs = 0;
-  bool HasFnAttr = false;
-  switch(PackedID & 0xFF) {{
-    default: llvm_unreachable("Invalid attribute number");
-)",
-                MaxNumAttrs);
 
-  for (const auto [IntPtr, UniqueID] : UniqAttributes) {
-    OS << formatv("  case {}:\n", UniqueID);
-    const CodeGenIntrinsic &Int = *IntPtr;
-
-    unsigned NumAttrs = 0;
-
-    for (const auto &[AttrIdx, Attrs] : enumerate(Int.ArgumentAttributes)) {
-      if (Attrs.empty())
-        continue;
+  // Construct an ArrayRef for easier range checking.
+  ArrayRef<ArgAttributesInfo> ArgAttributesInfoTableAR(ArgAttributesInfoTable);
+  if (ArgAttrID >= ArgAttributesInfoTableAR.size())
+    llvm_unreachable("Invalid arguments attribute ID");
 
-      unsigned ArgAttrID = UniqArgAttributes.find(Attrs)->second;
-      OS << formatv("    AS[{}] = {{{}, getIntrinsicArgAttributeSet(C, {}, "
-                    "FT->getContainedType({}))};\n",
-                    NumAttrs++, AttrIdx, ArgAttrID, AttrIdx);
-    }
-
-    if (hasFnAttributes(Int))
-      OS << "    HasFnAttr = true;\n";
-
-    if (NumAttrs)
-      OS << formatv("    NumAttrs = {};\n", NumAttrs);
-    OS << "    break;\n";
+  auto [StartIndex, NumAttrs] = ArgAttributesInfoTableAR[ArgAttrID];
+  for (unsigned Idx = 0; Idx < NumAttrs; ++Idx) {{
+    auto [ArgNo, ArgAttrID] = ArgAttrIdTable[StartIndex + Idx];
+    AS[Idx] = {{ArgNo,
+        getIntrinsicArgAttributeSet(C, ArgAttrID, FT->getContainedType(ArgNo))};
   }
-
-  OS << R"(  }
-  if (HasFnAttr) {
-    AS[NumAttrs++] = {AttributeList::FunctionIndex,
+  if (FnAttrID != {}) {
+    AS[NumAttrs++] = {{AttributeList::FunctionIndex,
                       getIntrinsicFnAttributeSet(C, FnAttrID)};
   }
   return AttributeList::get(C, ArrayRef(AS, NumAttrs));
 }
 #endif // GET_INTRINSIC_ATTRIBUTES
 
-)";
+)",
+                MaxNumAttrs, NoFunctionAttrsID);
 }
 
 void IntrinsicEmitter::EmitIntrinsicToBuiltinMap(



More information about the llvm-commits mailing list