[llvm] [AArch64][llvm] Generate asm parser extension map from TableGen (NFC) (PR #191045)

Jonathan Thackray via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 29 06:23:34 PDT 2026


https://github.com/jthackray updated https://github.com/llvm/llvm-project/pull/191045

>From 150f39fe8a38669b255da3b580bac61ea6e75141 Mon Sep 17 00:00:00 2001
From: Jonathan Thackray <jonathan.thackray at arm.com>
Date: Fri, 3 Apr 2026 01:47:50 +0100
Subject: [PATCH 1/2] [AArch64][llvm] Generate asm parser extension map from
 TableGen (NFC)

Modify ARMTargetDefEmitter to emit the assembly parser `ExtensionMap[]`
and replace the hand-written table in AArch64AsmParser.cpp with the
generated one.

Keep the assembly parser-only quirks in the emitter: the directive
denylist, the legacy primary spellings for `mte` and `compnum`, and the
extra bits needed for the `sve2-*` shorthand extensions.

This removes the duplicate manual table while preserving existing
directive parsing and required-feature diagnostics.
---
 .../AArch64/AsmParser/AArch64AsmParser.cpp    | 142 +-----------------
 .../TableGen/Basic/ARMTargetDefEmitter.cpp    |  97 ++++++++++++
 2 files changed, 102 insertions(+), 137 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
index 6a6b73b8a4c88..dddde9a67967e 100644
--- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
+++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
@@ -3827,140 +3827,8 @@ AArch64AsmParser::tryParseOptionalShiftExtend(OperandVector &Operands) {
   return ParseStatus::Success;
 }
 
-static const struct Extension {
-  const char *Name;
-  const FeatureBitset Features;
-} ExtensionMap[] = {
-    {"crc", {AArch64::FeatureCRC}},
-    {"sm4", {AArch64::FeatureSM4}},
-    {"sha3", {AArch64::FeatureSHA3}},
-    {"sha2", {AArch64::FeatureSHA2}},
-    {"aes", {AArch64::FeatureAES}},
-    {"crypto", {AArch64::FeatureCrypto}},
-    {"fp", {AArch64::FeatureFPARMv8}},
-    {"simd", {AArch64::FeatureNEON}},
-    {"ras", {AArch64::FeatureRAS}},
-    {"rasv2", {AArch64::FeatureRASv2}},
-    {"lse", {AArch64::FeatureLSE}},
-    {"predres", {AArch64::FeaturePredRes}},
-    {"predres2", {AArch64::FeatureSPECRES2}},
-    {"ccdp", {AArch64::FeatureCacheDeepPersist}},
-    {"mte", {AArch64::FeatureMTE}},
-    {"memtag", {AArch64::FeatureMTE}},
-    {"tlb-rmi", {AArch64::FeatureTLB_RMI}},
-    {"pan", {AArch64::FeaturePAN}},
-    {"pan-rwv", {AArch64::FeaturePAN_RWV}},
-    {"ccpp", {AArch64::FeatureCCPP}},
-    {"rcpc", {AArch64::FeatureRCPC}},
-    {"rng", {AArch64::FeatureRandGen}},
-    {"sve", {AArch64::FeatureSVE}},
-    {"sve-b16b16", {AArch64::FeatureSVEB16B16}},
-    {"sve2", {AArch64::FeatureSVE2}},
-    {"sve-aes", {AArch64::FeatureSVEAES}},
-    {"sve2-aes", {AArch64::FeatureAliasSVE2AES, AArch64::FeatureSVEAES}},
-    {"sve-sm4", {AArch64::FeatureSVESM4}},
-    {"sve2-sm4", {AArch64::FeatureAliasSVE2SM4, AArch64::FeatureSVESM4}},
-    {"sve-sha3", {AArch64::FeatureSVESHA3}},
-    {"sve2-sha3", {AArch64::FeatureAliasSVE2SHA3, AArch64::FeatureSVESHA3}},
-    {"sve-bitperm", {AArch64::FeatureSVEBitPerm}},
-    {"sve2-bitperm",
-     {AArch64::FeatureAliasSVE2BitPerm, AArch64::FeatureSVEBitPerm,
-      AArch64::FeatureSVE2}},
-    {"sve2p1", {AArch64::FeatureSVE2p1}},
-    {"ls64", {AArch64::FeatureLS64}},
-    {"xs", {AArch64::FeatureXS}},
-    {"pauth", {AArch64::FeaturePAuth}},
-    {"flagm", {AArch64::FeatureFlagM}},
-    {"rme", {AArch64::FeatureRME}},
-    {"sme", {AArch64::FeatureSME}},
-    {"sme-f64f64", {AArch64::FeatureSMEF64F64}},
-    {"sme-f16f16", {AArch64::FeatureSMEF16F16}},
-    {"sme-i16i64", {AArch64::FeatureSMEI16I64}},
-    {"sme2", {AArch64::FeatureSME2}},
-    {"sme2p1", {AArch64::FeatureSME2p1}},
-    {"sme-b16b16", {AArch64::FeatureSMEB16B16}},
-    {"hbc", {AArch64::FeatureHBC}},
-    {"mops", {AArch64::FeatureMOPS}},
-    {"mec", {AArch64::FeatureMEC}},
-    {"the", {AArch64::FeatureTHE}},
-    {"d128", {AArch64::FeatureD128}},
-    {"lse128", {AArch64::FeatureLSE128}},
-    {"ite", {AArch64::FeatureITE}},
-    {"cssc", {AArch64::FeatureCSSC}},
-    {"rcpc3", {AArch64::FeatureRCPC3}},
-    {"gcs", {AArch64::FeatureGCS}},
-    {"bf16", {AArch64::FeatureBF16}},
-    {"compnum", {AArch64::FeatureComplxNum}},
-    {"dotprod", {AArch64::FeatureDotProd}},
-    {"f32mm", {AArch64::FeatureMatMulFP32}},
-    {"f64mm", {AArch64::FeatureMatMulFP64}},
-    {"fp16", {AArch64::FeatureFullFP16}},
-    {"fp16fml", {AArch64::FeatureFP16FML}},
-    {"i8mm", {AArch64::FeatureMatMulInt8}},
-    {"lor", {AArch64::FeatureLOR}},
-    {"profile", {AArch64::FeatureSPE}},
-    // "rdma" is the name documented by binutils for the feature, but
-    // binutils also accepts incomplete prefixes of features, so "rdm"
-    // works too. Support both spellings here.
-    {"rdm", {AArch64::FeatureRDM}},
-    {"rdma", {AArch64::FeatureRDM}},
-    {"sb", {AArch64::FeatureSB}},
-    {"ssbs", {AArch64::FeatureSSBS}},
-    {"fp8", {AArch64::FeatureFP8}},
-    {"faminmax", {AArch64::FeatureFAMINMAX}},
-    {"fp8fma", {AArch64::FeatureFP8FMA}},
-    {"ssve-fp8fma", {AArch64::FeatureSSVE_FP8FMA}},
-    {"fp8dot2", {AArch64::FeatureFP8DOT2}},
-    {"ssve-fp8dot2", {AArch64::FeatureSSVE_FP8DOT2}},
-    {"fp8dot4", {AArch64::FeatureFP8DOT4}},
-    {"ssve-fp8dot4", {AArch64::FeatureSSVE_FP8DOT4}},
-    {"lut", {AArch64::FeatureLUT}},
-    {"sme-lutv2", {AArch64::FeatureSME_LUTv2}},
-    {"sme-f8f16", {AArch64::FeatureSMEF8F16}},
-    {"sme-f8f32", {AArch64::FeatureSMEF8F32}},
-    {"sme-fa64", {AArch64::FeatureSMEFA64}},
-    {"cpa", {AArch64::FeatureCPA}},
-    {"tlbiw", {AArch64::FeatureTLBIW}},
-    {"pops", {AArch64::FeaturePoPS}},
-    {"cmpbr", {AArch64::FeatureCMPBR}},
-    {"f8f32mm", {AArch64::FeatureF8F32MM}},
-    {"f8f16mm", {AArch64::FeatureF8F16MM}},
-    {"fprcvt", {AArch64::FeatureFPRCVT}},
-    {"lsfe", {AArch64::FeatureLSFE}},
-    {"sme2p2", {AArch64::FeatureSME2p2}},
-    {"ssve-aes", {AArch64::FeatureSSVE_AES}},
-    {"sve2p2", {AArch64::FeatureSVE2p2}},
-    {"sve-aes2", {AArch64::FeatureSVEAES2}},
-    {"sve-bfscale", {AArch64::FeatureSVEBFSCALE}},
-    {"sve-f16f32mm", {AArch64::FeatureSVE_F16F32MM}},
-    {"lsui", {AArch64::FeatureLSUI}},
-    {"occmo", {AArch64::FeatureOCCMO}},
-    {"ssve-bitperm", {AArch64::FeatureSSVE_BitPerm}},
-    {"sme-mop4", {AArch64::FeatureSME_MOP4}},
-    {"sme-tmop", {AArch64::FeatureSME_TMOP}},
-    {"lscp", {AArch64::FeatureLSCP}},
-    {"tlbid", {AArch64::FeatureTLBID}},
-    {"mtetc", {AArch64::FeatureMTETC}},
-    {"gcie", {AArch64::FeatureGCIE}},
-    {"sme2p3", {AArch64::FeatureSME2p3}},
-    {"sve2p3", {AArch64::FeatureSVE2p3}},
-    {"sve-b16mm", {AArch64::FeatureSVE_B16MM}},
-    {"f16mm", {AArch64::FeatureF16MM}},
-    {"f16f32dot", {AArch64::FeatureF16F32DOT}},
-    {"f16f32mm", {AArch64::FeatureF16F32MM}},
-    {"mops-go", {AArch64::FeatureMOPS_GO}},
-    {"poe2", {AArch64::FeatureS1POE2}},
-    {"tev", {AArch64::FeatureTEV}},
-    {"btie", {AArch64::FeatureBTIE}},
-    {"dit", {AArch64::FeatureDIT}},
-    {"brbe", {AArch64::FeatureBRBE}},
-    {"bti", {AArch64::FeatureBranchTargetId}},
-    {"fcma", {AArch64::FeatureComplxNum}},
-    {"jscvt", {AArch64::FeatureJS}},
-    {"pauth-lr", {AArch64::FeaturePAuthLR}},
-    {"ssve-fexpa", {AArch64::FeatureSSVE_FEXPA}},
-    {"wfxt", {AArch64::FeatureWFxT}},
-};
+#define EMIT_ASM_PARSER_EXTENSIONS
+#include "llvm/TargetParser/AArch64TargetParserDef.inc"
 
 static void setRequiredFeatureString(FeatureBitset FBS, std::string &Str) {
   if (FBS[AArch64::HasV8_0aOps])
@@ -7466,7 +7334,7 @@ bool AArch64AsmParser::parseDirectiveArch(SMLoc L) {
     bool EnableFeature = !Name.consume_front_insensitive("no");
 
     auto It = llvm::find_if(ExtensionMap, [&Name](const auto &Extension) {
-      return Extension.Name == Name;
+      return Extension.Name == Name && Extension.AllowInDirective;
     });
 
     if (It == std::end(ExtensionMap))
@@ -7503,7 +7371,7 @@ bool AArch64AsmParser::parseDirectiveArchExtension(SMLoc L) {
   }
 
   auto It = llvm::find_if(ExtensionMap, [&Name](const auto &Extension) {
-    return Extension.Name == Name;
+    return Extension.Name == Name && Extension.AllowInDirective;
   });
 
   if (It == std::end(ExtensionMap))
@@ -7555,7 +7423,7 @@ bool AArch64AsmParser::parseDirectiveCPU(SMLoc L) {
     bool EnableFeature = !Name.consume_front_insensitive("no");
 
     auto It = llvm::find_if(ExtensionMap, [&Name](const auto &Extension) {
-      return Extension.Name == Name;
+      return Extension.Name == Name && Extension.AllowInDirective;
     });
 
     if (It == std::end(ExtensionMap))
diff --git a/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp b/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp
index a18fed082207f..b361186a7e4b8 100644
--- a/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp
@@ -14,6 +14,7 @@
 
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/StringSet.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "llvm/Support/Format.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/TableGen/Error.h"
@@ -53,6 +54,66 @@ static void checkFeatureTree(const Record *Root) {
   }
 }
 
+static bool allowInAArch64AsmParserDirective(const Record *Rec) {
+  // The generated asm parser extension table is used for both directive lookup
+  // and required-feature diagnostics. Some extensions still need to appear in
+  // diagnostics, but must not be accepted by .arch/.arch_extension/.cpu.
+  return StringSwitch<bool>(Rec->getName())
+      .Case("FeaturePerfMon", false)
+      .Case("FeatureSpecRestrict", false)
+      .Case("FeatureVH", false)
+      .Case("FeatureEnhancedCounterVirtualization", false)
+      .Case("FeaturePsUAO", false)
+      .Case("FeatureFPAC", false)
+      .Case("FeatureCCIDX", false)
+      .Case("FeatureNV", false)
+      .Case("FeatureLSE2", false)
+      .Case("FeatureMPAM", false)
+      .Case("FeatureTRACEV8_4", false)
+      .Case("FeatureAM", false)
+      .Case("FeatureSEL2", false)
+      .Case("FeatureRCPC_IMMO", false)
+      .Case("FeatureAltFPCmp", false)
+      .Case("FeatureFRInt3264", false)
+      .Case("FeatureAMVS", false)
+      .Case("FeatureFineGrainedTraps", false)
+      .Case("FeatureHCX", false)
+      .Case("FeatureSPE_EEF", false)
+      .Case("FeatureNMI", false)
+      .Case("FeatureCLRBHB", false)
+      .Case("FeaturePRFM_SLC", false)
+      .Case("FeatureTRBE", false)
+      .Case("FeatureETE", false)
+      .Case("FeatureCHK", false)
+      .Default(true);
+}
+
+static StringRef getAArch64AsmParserPrimaryName(const Record *Rec) {
+  // Preserve legacy GNU-style spellings where the asm parser intentionally
+  // differs from the target parser's user-visible extension name.
+  return StringSwitch<StringRef>(Rec->getName())
+      .Case("FeatureComplxNum", "compnum")
+      .Case("FeatureMTE", "mte")
+      .Default("");
+}
+
+static ArrayRef<StringRef> getAArch64AsmParserExtraFeatures(const Record *Rec) {
+  // These shorthands historically toggled extra feature bits in the
+  // hand-written asm parser extension table, so keep the same behavior here.
+  static constexpr StringRef SVE2AES[] = {"FeatureSVEAES"};
+  static constexpr StringRef SVE2SM4[] = {"FeatureSVESM4"};
+  static constexpr StringRef SVE2SHA3[] = {"FeatureSVESHA3"};
+  static constexpr StringRef SVE2BitPerm[] = {"FeatureSVEBitPerm",
+                                              "FeatureSVE2"};
+
+  return StringSwitch<ArrayRef<StringRef>>(Rec->getName())
+      .Case("FeatureAliasSVE2AES", ArrayRef(SVE2AES))
+      .Case("FeatureAliasSVE2SM4", ArrayRef(SVE2SM4))
+      .Case("FeatureAliasSVE2SHA3", ArrayRef(SVE2SHA3))
+      .Case("FeatureAliasSVE2BitPerm", ArrayRef(SVE2BitPerm))
+      .Default({});
+}
+
 static void emitARMTargetDef(const RecordKeeper &RK, raw_ostream &OS) {
   OS << "// Autogenerated by ARMTargetDefEmitter.cpp\n\n";
 
@@ -150,6 +211,42 @@ static void emitARMTargetDef(const RecordKeeper &RK, raw_ostream &OS) {
      << "#endif // EMIT_EXTENSIONS\n"
      << "\n";
 
+  OS << "#ifdef EMIT_ASM_PARSER_EXTENSIONS\n"
+     << "static const struct Extension {\n"
+     << "  const char *Name;\n"
+     << "  const FeatureBitset Features;\n"
+     << "  bool AllowInDirective;\n"
+     << "} ExtensionMap[] = {\n";
+  for (const Record *Rec : SortedExtensions) {
+    bool AllowInDirective = allowInAArch64AsmParserDirective(Rec);
+    auto EmitEntry = [&](StringRef Name) {
+      if (Name.empty())
+        return;
+      OS << "  {\"" << Name << "\", {AArch64::" << Rec->getName();
+      for (StringRef Extra : getAArch64AsmParserExtraFeatures(Rec))
+        OS << ", AArch64::" << Extra;
+      OS << "}, " << (AllowInDirective ? "true" : "false") << "},\n";
+    };
+
+    StringRef PrimaryName = getAArch64AsmParserPrimaryName(Rec);
+    if (PrimaryName.empty())
+      PrimaryName = Rec->getValueAsString("UserVisibleName");
+    if (PrimaryName.empty())
+      PrimaryName = Rec->getValueAsString("Name");
+    EmitEntry(PrimaryName);
+
+    if (auto Name = Rec->getValueAsString("UserVisibleName");
+        !Name.empty() && Name != PrimaryName)
+      EmitEntry(Name);
+    if (auto Alias = Rec->getValueAsString("UserVisibleAlias");
+        !Alias.empty() && Alias != PrimaryName)
+      EmitEntry(Alias);
+  }
+  OS << "};\n"
+     << "#undef EMIT_ASM_PARSER_EXTENSIONS\n"
+     << "#endif // EMIT_ASM_PARSER_EXTENSIONS\n"
+     << "\n";
+
   // Emit FMV information
   auto FMVExts = RK.getAllDerivedDefinitionsIfDefined("FMVExtension");
   OS << "#ifdef EMIT_FMV_INFO\n"

>From 97d6d0b1f4ac86e906eb4bc32df8a68528b895da Mon Sep 17 00:00:00 2001
From: Jonathan Thackray <jonathan.thackray at arm.com>
Date: Wed, 29 Apr 2026 14:10:51 +0100
Subject: [PATCH 2/2] fixup! Use SmallDenseSet<> for quick lookup

---
 .../TableGen/Basic/ARMTargetDefEmitter.cpp    | 58 ++++++++++---------
 1 file changed, 30 insertions(+), 28 deletions(-)

diff --git a/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp b/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp
index b361186a7e4b8..1773971376c40 100644
--- a/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp
@@ -13,6 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/Support/Format.h"
@@ -58,34 +59,35 @@ static bool allowInAArch64AsmParserDirective(const Record *Rec) {
   // The generated asm parser extension table is used for both directive lookup
   // and required-feature diagnostics. Some extensions still need to appear in
   // diagnostics, but must not be accepted by .arch/.arch_extension/.cpu.
-  return StringSwitch<bool>(Rec->getName())
-      .Case("FeaturePerfMon", false)
-      .Case("FeatureSpecRestrict", false)
-      .Case("FeatureVH", false)
-      .Case("FeatureEnhancedCounterVirtualization", false)
-      .Case("FeaturePsUAO", false)
-      .Case("FeatureFPAC", false)
-      .Case("FeatureCCIDX", false)
-      .Case("FeatureNV", false)
-      .Case("FeatureLSE2", false)
-      .Case("FeatureMPAM", false)
-      .Case("FeatureTRACEV8_4", false)
-      .Case("FeatureAM", false)
-      .Case("FeatureSEL2", false)
-      .Case("FeatureRCPC_IMMO", false)
-      .Case("FeatureAltFPCmp", false)
-      .Case("FeatureFRInt3264", false)
-      .Case("FeatureAMVS", false)
-      .Case("FeatureFineGrainedTraps", false)
-      .Case("FeatureHCX", false)
-      .Case("FeatureSPE_EEF", false)
-      .Case("FeatureNMI", false)
-      .Case("FeatureCLRBHB", false)
-      .Case("FeaturePRFM_SLC", false)
-      .Case("FeatureTRBE", false)
-      .Case("FeatureETE", false)
-      .Case("FeatureCHK", false)
-      .Default(true);
+  static const SmallDenseSet<StringRef, 32> DisallowedExtensionNames = {
+      "FeatureAltFPCmp",
+      "FeatureAM",
+      "FeatureAMVS",
+      "FeatureCCIDX",
+      "FeatureCHK",
+      "FeatureCLRBHB",
+      "FeatureEnhancedCounterVirtualization",
+      "FeatureETE",
+      "FeatureFineGrainedTraps",
+      "FeatureFPAC",
+      "FeatureFRInt3264",
+      "FeatureHCX",
+      "FeatureLSE2",
+      "FeatureMPAM",
+      "FeatureNMI",
+      "FeatureNV",
+      "FeaturePerfMon",
+      "FeaturePRFM_SLC",
+      "FeaturePsUAO",
+      "FeatureRCPC_IMMO",
+      "FeatureSEL2",
+      "FeatureSPE_EEF",
+      "FeatureSpecRestrict",
+      "FeatureTRACEV8_4",
+      "FeatureTRBE",
+      "FeatureVH",
+  };
+  return !DisallowedExtensionNames.contains(Rec->getName());
 }
 
 static StringRef getAArch64AsmParserPrimaryName(const Record *Rec) {



More information about the llvm-commits mailing list