[llvm] [TableGen] Simplify generated code for isSubclass (PR #117351)

Jay Foad via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 27 04:38:04 PST 2024


https://github.com/jayfoad updated https://github.com/llvm/llvm-project/pull/117351

>From 86cadae79ae9c89ca93dc0862dbbb983ca1d3085 Mon Sep 17 00:00:00 2001
From: Jay Foad <jay.foad at amd.com>
Date: Fri, 22 Nov 2024 16:33:44 +0000
Subject: [PATCH 1/5] [TableGen] Simplify generated code for isSubclass

Implement isSubclass with some tables and a single call to lower_bound
instead of nested switches.

Part of the motivation for this is improving compile time when clang-18
is used as a host compiler, since it seems to have trouble with very
large switch statements.
---
 llvm/utils/TableGen/AsmMatcherEmitter.cpp | 50 ++++++++---------------
 1 file changed, 18 insertions(+), 32 deletions(-)

diff --git a/llvm/utils/TableGen/AsmMatcherEmitter.cpp b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
index ade393c11b7a24..68e9b434312a80 100644
--- a/llvm/utils/TableGen/AsmMatcherEmitter.cpp
+++ b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
@@ -2541,7 +2541,6 @@ static void emitIsSubclass(CodeGenTarget &Target,
   OS << "  if (A == B)\n";
   OS << "    return true;\n\n";
 
-  bool EmittedSwitch = false;
   for (const auto &A : Infos) {
     std::vector<StringRef> SuperClasses;
     if (A.IsOptional)
@@ -2551,42 +2550,29 @@ static void emitIsSubclass(CodeGenTarget &Target,
         SuperClasses.push_back(B.Name);
     }
 
-    if (SuperClasses.empty())
+    if (SuperClasses.empty()) {
+      OS << "  static constexpr ArrayRef<uint16_t> " << A.Name << "_SuperClasses;\n";
       continue;
-
-    // If this is the first SuperClass, emit the switch header.
-    if (!EmittedSwitch) {
-      OS << "  switch (A) {\n";
-      OS << "  default:\n";
-      OS << "    return false;\n";
-      EmittedSwitch = true;
     }
 
-    OS << "\n  case " << A.Name << ":\n";
-
-    if (SuperClasses.size() == 1) {
-      OS << "    return B == " << SuperClasses.back() << ";\n";
-      continue;
-    }
-
-    if (!SuperClasses.empty()) {
-      OS << "    switch (B) {\n";
-      OS << "    default: return false;\n";
-      for (StringRef SC : SuperClasses)
-        OS << "    case " << SC << ": return true;\n";
-      OS << "    }\n";
-    } else {
-      // No case statement to emit
-      OS << "    return false;\n";
-    }
+    OS << "  static constexpr uint16_t " << A.Name << "_SuperClasses[] = {";
+    ListSeparator LS;
+    for (auto &SC : SuperClasses)
+      OS << LS << SC;
+    OS << "};\n";
   }
+  OS << "\n";
 
-  // If there were case statements emitted into the string stream write the
-  // default.
-  if (EmittedSwitch)
-    OS << "  }\n";
-  else
-    OS << "  return false;\n";
+  OS << "  static constexpr ArrayRef<uint16_t> SuperClassTable[] = {\n";
+  OS << "    {}, // InvalidMatchClass\n";
+  OS << "    {}, // OptionalMatchClass\n";
+  for (const auto &A : Infos)
+    OS << "    " << A.Name << "_SuperClasses,\n";
+  OS << "  };\n\n";
+
+  OS << "  ArrayRef<uint16_t> SuperClasses = SuperClassTable[(unsigned)A];\n";
+  OS << "  const uint16_t *It = lower_bound(SuperClasses, (unsigned)B);\n";
+  OS << "  return It != SuperClasses.end() && *It == (unsigned)B;\n";
 
   OS << "}\n\n";
 }

>From eafb850dafed1b54cd02c543a8f205314fd4e908 Mon Sep 17 00:00:00 2001
From: Jay Foad <jay.foad at amd.com>
Date: Mon, 25 Nov 2024 12:10:08 +0000
Subject: [PATCH 2/5] clang-format

---
 llvm/utils/TableGen/AsmMatcherEmitter.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/utils/TableGen/AsmMatcherEmitter.cpp b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
index 68e9b434312a80..df9cd9388360b8 100644
--- a/llvm/utils/TableGen/AsmMatcherEmitter.cpp
+++ b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
@@ -2551,7 +2551,8 @@ static void emitIsSubclass(CodeGenTarget &Target,
     }
 
     if (SuperClasses.empty()) {
-      OS << "  static constexpr ArrayRef<uint16_t> " << A.Name << "_SuperClasses;\n";
+      OS << "  static constexpr ArrayRef<uint16_t> " << A.Name
+         << "_SuperClasses;\n";
       continue;
     }
 

>From 6d59a91ddaba6cd5ba5d47f197e1747d32f71ad3 Mon Sep 17 00:00:00 2001
From: Jay Foad <jay.foad at amd.com>
Date: Mon, 25 Nov 2024 12:09:39 +0000
Subject: [PATCH 3/5] Use binary_search

---
 llvm/utils/TableGen/AsmMatcherEmitter.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/utils/TableGen/AsmMatcherEmitter.cpp b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
index df9cd9388360b8..be75a157033182 100644
--- a/llvm/utils/TableGen/AsmMatcherEmitter.cpp
+++ b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
@@ -2572,8 +2572,7 @@ static void emitIsSubclass(CodeGenTarget &Target,
   OS << "  };\n\n";
 
   OS << "  ArrayRef<uint16_t> SuperClasses = SuperClassTable[(unsigned)A];\n";
-  OS << "  const uint16_t *It = lower_bound(SuperClasses, (unsigned)B);\n";
-  OS << "  return It != SuperClasses.end() && *It == (unsigned)B;\n";
+  OS << "  return binary_search(SuperClasses, (unsigned)B);\n";
 
   OS << "}\n\n";
 }

>From 912547b94fe23f36decdba8fd92dd71cb4eb4020 Mon Sep 17 00:00:00 2001
From: Jay Foad <jay.foad at amd.com>
Date: Mon, 25 Nov 2024 12:36:20 +0000
Subject: [PATCH 4/5] Remove casts to unsigned

---
 llvm/utils/TableGen/AsmMatcherEmitter.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/utils/TableGen/AsmMatcherEmitter.cpp b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
index be75a157033182..7c5ad517d4e90f 100644
--- a/llvm/utils/TableGen/AsmMatcherEmitter.cpp
+++ b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
@@ -2571,8 +2571,8 @@ static void emitIsSubclass(CodeGenTarget &Target,
     OS << "    " << A.Name << "_SuperClasses,\n";
   OS << "  };\n\n";
 
-  OS << "  ArrayRef<uint16_t> SuperClasses = SuperClassTable[(unsigned)A];\n";
-  OS << "  return binary_search(SuperClasses, (unsigned)B);\n";
+  OS << "  ArrayRef<uint16_t> SuperClasses = SuperClassTable[A];\n";
+  OS << "  return binary_search(SuperClasses, B);\n";
 
   OS << "}\n\n";
 }

>From 3e5035b07f42c82ed68e1a13bc4c6d7bb7e678bd Mon Sep 17 00:00:00 2001
From: Jay Foad <jay.foad at amd.com>
Date: Wed, 27 Nov 2024 11:59:46 +0000
Subject: [PATCH 5/5] Use bit table lookup

---
 llvm/utils/TableGen/AsmMatcherEmitter.cpp | 72 ++++++++++++++---------
 1 file changed, 45 insertions(+), 27 deletions(-)

diff --git a/llvm/utils/TableGen/AsmMatcherEmitter.cpp b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
index 7c5ad517d4e90f..b68d72572b60f0 100644
--- a/llvm/utils/TableGen/AsmMatcherEmitter.cpp
+++ b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
@@ -2541,39 +2541,57 @@ static void emitIsSubclass(CodeGenTarget &Target,
   OS << "  if (A == B)\n";
   OS << "    return true;\n\n";
 
+  // TODO: Use something like SequenceToOffsetTable to allow sequences to
+  // overlap in this table.
+  SmallVector<bool> SuperClassData;
+
+  OS << "  [[maybe_unused]] static constexpr struct {\n";
+  OS << "    uint32_t Offset;\n";
+  OS << "    uint16_t Start;\n";
+  OS << "    uint16_t Length;\n";
+  OS << "  } Table[] = {\n";
+  OS << "    {0, 0, 0},\n"; // InvalidMatchClass
+  OS << "    {0, 0, 0},\n"; // OptionalMatchClass
   for (const auto &A : Infos) {
-    std::vector<StringRef> SuperClasses;
-    if (A.IsOptional)
-      SuperClasses.push_back("OptionalMatchClass");
-    for (const auto &B : Infos) {
-      if (&A != &B && A.isSubsetOf(B))
-        SuperClasses.push_back(B.Name);
-    }
+    SmallVector<bool> SuperClasses;
+    SuperClasses.push_back(false); // InvalidMatchClass
+    SuperClasses.push_back(A.IsOptional); // OptionalMatchClass
+    for (const auto &B : Infos)
+      SuperClasses.push_back(&A != &B && A.isSubsetOf(B));
 
-    if (SuperClasses.empty()) {
-      OS << "  static constexpr ArrayRef<uint16_t> " << A.Name
-         << "_SuperClasses;\n";
-      continue;
-    }
+    // Trim leading and trailing zeros.
+    auto End = find_if(reverse(SuperClasses), [](bool B){ return B; }).base();
+    auto Start = std::find_if(SuperClasses.begin(), End, [](bool B){ return B; });
 
-    OS << "  static constexpr uint16_t " << A.Name << "_SuperClasses[] = {";
-    ListSeparator LS;
-    for (auto &SC : SuperClasses)
-      OS << LS << SC;
-    OS << "};\n";
-  }
-  OS << "\n";
+    unsigned Offset = SuperClassData.size();
+    SuperClassData.append(Start, End);
 
-  OS << "  static constexpr ArrayRef<uint16_t> SuperClassTable[] = {\n";
-  OS << "    {}, // InvalidMatchClass\n";
-  OS << "    {}, // OptionalMatchClass\n";
-  for (const auto &A : Infos)
-    OS << "    " << A.Name << "_SuperClasses,\n";
+    OS << "    {" << Offset << ", " << (Start - SuperClasses.begin()) << ", " <<
+        (End - Start) << "},\n";
+  }
   OS << "  };\n\n";
 
-  OS << "  ArrayRef<uint16_t> SuperClasses = SuperClassTable[A];\n";
-  OS << "  return binary_search(SuperClasses, B);\n";
-
+  if (SuperClassData.empty()) {
+    OS << "  return false;\n";
+  } else {
+    // Dump the boolean data packed into bytes.
+    SuperClassData.append(-SuperClassData.size() % 8, false);
+    OS << "  static constexpr uint8_t Data[] = {\n";
+    for (unsigned I = 0, E = SuperClassData.size(); I < E; I += 8) {
+      unsigned Byte = 0;
+      for (unsigned J = 0; J < 8; ++J)
+        Byte |= (unsigned)SuperClassData[I + J] << J;
+      OS << formatv("    {:X2},\n", Byte);
+    }
+    OS << "  };\n\n";
+
+    OS << "  auto &Entry = Table[A];\n";
+    OS << "  unsigned Idx = B - Entry.Start;\n";
+    OS << "  if (Idx >= Entry.Length)\n";
+    OS << "    return false;\n";
+    OS << "  Idx += Entry.Offset;\n";
+    OS << "  return (Data[Idx / 8] >> (Idx % 8)) & 1;\n";
+  }
   OS << "}\n\n";
 }
 



More information about the llvm-commits mailing list