[llvm] [AMDGPU][NFC] Allow selected generic features on any covered GPU (PR #223177)

Chinmay Deshpande via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 17 10:50:38 PDT 2026


https://github.com/chinmaydd updated https://github.com/llvm/llvm-project/pull/223177

>From fc1b4754ec642e1f37ac516f94f309c5f0156743 Mon Sep 17 00:00:00 2001
From: Chinmay Deshpande <chdeshpa at amd.com>
Date: Mon, 14 Sep 2026 10:50:33 -0700
Subject: [PATCH] [AMDGPU][NFC] Allow selected generic features on any covered
 GPU

Add AMDGPUGenericAnyFeature so individual feature classes can require
support from at least one covered GPU. Check explicitly marked features
even when they are not frontend-visible, while retaining the all-member
check for ordinary frontend-visible features.

Follow implied features using their own validation rules. Require named
generic targets to cover at least one concrete AMDGPU GPU, rejecting self
references, other generic targets, pseudo targets, and invalid members.

Cover partial support, missing support, exact numeric-feature membership,
backend-only features, implications, and malformed coverage lists with
TableGen tests. Generated target definitions remain unchanged.

Change-Id: Id6ff521e92750a953be54dcc89d98e1b94eb16e2
---
 llvm/lib/Target/AMDGPU/AMDGPUTargetParser.td  |  19 +-
 llvm/test/TableGen/AMDGPUTargetDefErrors.td   |   2 +-
 .../AMDGPUTargetDefGenericFeatures.td         | 166 ++++++++++++++++++
 .../TableGen/Basic/AMDGPUTargetDefEmitter.cpp |  55 ++++--
 4 files changed, 219 insertions(+), 23 deletions(-)
 create mode 100644 llvm/test/TableGen/AMDGPUTargetDefGenericFeatures.td

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUTargetParser.td b/llvm/lib/Target/AMDGPU/AMDGPUTargetParser.td
index 575baaf2ec0aa..d32a40a5309d1 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUTargetParser.td
+++ b/llvm/lib/Target/AMDGPU/AMDGPUTargetParser.td
@@ -6,11 +6,21 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// TargetParser metadata attached to processor records for the
-// -gen-amdgpu-target-def backend.
+// TargetParser metadata for the -gen-amdgpu-target-def backend.
 //
 //===----------------------------------------------------------------------===//
 
+// A generic target may use this feature if at least one covered GPU has it.
+// These features participate in validation even when not frontend-visible.
+// Other frontend-visible features must be present on every covered GPU.
+// Implied features are checked according to their own classes.
+// This checks feature membership, not numeric ordering of feature values.
+class AMDGPUGenericAnyFeature<string name, string fieldName, string value,
+                             string description,
+                             list<SubtargetFeature> implies = [],
+                             InlineBehavior ib = InlineDefault>
+    : SubtargetFeature<name, fieldName, value, description, implies, ib>;
+
 // Marks a Processor/ProcessorModel record as a canonical GPU.
 //
 // \p isa is the ISA version [major, minor, stepping]. Empty for R600 (no AMDGCN
@@ -18,9 +28,8 @@
 class AMDGPUGPUInfo<list<int> isa = []> {
   list<int> IsaVersion = isa;
 
-  // List of targets which are compatible with this target. This
-  // should be used for a "gfxN-generic" targets only, and empty for
-  // individual GPUs.
+  // For a "gfxN-generic" target, this must contain at least one compatible
+  // concrete AMDGPU GPU. For individual GPUs and pseudo targets, it must be empty.
   list<Processor> CoveredGPUs = [];
 
   // A pseudo target ("generic"/"generic-hsa") that represents no
diff --git a/llvm/test/TableGen/AMDGPUTargetDefErrors.td b/llvm/test/TableGen/AMDGPUTargetDefErrors.td
index fc6180fbf9e68..9722c8cce9d44 100644
--- a/llvm/test/TableGen/AMDGPUTargetDefErrors.td
+++ b/llvm/test/TableGen/AMDGPUTargetDefErrors.td
@@ -151,7 +151,7 @@ class AMDGPUGPUInfo<list<int> isa = []> {
 
 def FeatureFoo : SubtargetFeature<"foo", "HasFoo", "true", "Foo">;
 
-// The generic check only considers frontend-visible features.
+// Ordinary frontend-visible features require support from every covered GPU.
 def AMDGPUFrontendVisibleFeatures {
   list<SubtargetFeature> Features = [FeatureFoo];
 }
diff --git a/llvm/test/TableGen/AMDGPUTargetDefGenericFeatures.td b/llvm/test/TableGen/AMDGPUTargetDefGenericFeatures.td
new file mode 100644
index 0000000000000..d1520f12d29b9
--- /dev/null
+++ b/llvm/test/TableGen/AMDGPUTargetDefGenericFeatures.td
@@ -0,0 +1,166 @@
+// RUN: split-file %s %t
+// RUN: llvm-tblgen -gen-amdgpu-target-def -I %t -I %p/../../include -I %p/../../lib/Target/AMDGPU %t/valid.td \
+// RUN:   | FileCheck %t/valid.td
+// RUN: not llvm-tblgen -gen-amdgpu-target-def -I %t -I %p/../../include -I %p/../../lib/Target/AMDGPU %t/missing-any.td 2>&1 \
+// RUN:   | FileCheck %t/missing-any.td -DFILE=%t/missing-any.td --implicit-check-not="error:"
+// RUN: not llvm-tblgen -gen-amdgpu-target-def -I %t -I %p/../../include -I %p/../../lib/Target/AMDGPU %t/missing-all.td 2>&1 \
+// RUN:   | FileCheck %t/missing-all.td -DFILE=%t/missing-all.td --implicit-check-not="error:"
+// RUN: not llvm-tblgen -gen-amdgpu-target-def -I %t -I %p/../../include -I %p/../../lib/Target/AMDGPU %t/implied-all.td 2>&1 \
+// RUN:   | FileCheck %t/implied-all.td -DFILE=%t/implied-all.td --implicit-check-not="error:"
+// RUN: not llvm-tblgen -gen-amdgpu-target-def -I %t -I %p/../../include -I %p/../../lib/Target/AMDGPU %t/missing-private.td 2>&1 \
+// RUN:   | FileCheck %t/missing-private.td -DFILE=%t/missing-private.td --implicit-check-not="error:"
+// RUN: not llvm-tblgen -gen-amdgpu-target-def -I %t -I %p/../../include -I %p/../../lib/Target/AMDGPU %t/numeric-mismatch.td 2>&1 \
+// RUN:   | FileCheck %t/numeric-mismatch.td -DFILE=%t/numeric-mismatch.td --implicit-check-not="error:"
+// RUN: not llvm-tblgen -gen-amdgpu-target-def -I %t -I %p/../../include -I %p/../../lib/Target/AMDGPU %t/empty-members.td 2>&1 \
+// RUN:   | FileCheck %t/empty-members.td -DFILE=%t/empty-members.td --implicit-check-not="error:"
+// RUN: not llvm-tblgen -gen-amdgpu-target-def -I %t -I %p/../../include -I %p/../../lib/Target/AMDGPU %t/self-member.td 2>&1 \
+// RUN:   | FileCheck %t/self-member.td -DFILE=%t/self-member.td --implicit-check-not="error:"
+// RUN: not llvm-tblgen -gen-amdgpu-target-def -I %t -I %p/../../include -I %p/../../lib/Target/AMDGPU %t/generic-member.td 2>&1 \
+// RUN:   | FileCheck %t/generic-member.td -DFILE=%t/generic-member.td --implicit-check-not="error:"
+// RUN: not llvm-tblgen -gen-amdgpu-target-def -I %t -I %p/../../include -I %p/../../lib/Target/AMDGPU %t/pseudo-member.td 2>&1 \
+// RUN:   | FileCheck %t/pseudo-member.td -DFILE=%t/pseudo-member.td --implicit-check-not="error:"
+// RUN: not llvm-tblgen -gen-amdgpu-target-def -I %t -I %p/../../include -I %p/../../lib/Target/AMDGPU %t/unregistered-member.td 2>&1 \
+// RUN:   | FileCheck %t/unregistered-member.td -DFILE=%t/unregistered-member.td --implicit-check-not="error:"
+// RUN: not llvm-tblgen -gen-amdgpu-target-def -I %t -I %p/../../include -I %p/../../lib/Target/AMDGPU %t/r600-member.td 2>&1 \
+// RUN:   | FileCheck %t/r600-member.td -DFILE=%t/r600-member.td --implicit-check-not="error:"
+
+//--- common.td
+include "llvm/Target/Target.td"
+include "AMDGPUTargetParser.td"
+def MyTarget : Target;
+
+def FeatureAll : SubtargetFeature<"all", "HasAll", "true", "">;
+def FeatureAnyA : AMDGPUGenericAnyFeature<"any-a", "HasAnyA", "true", "">;
+// Check that subclasses of the marker also opt in, including numeric features.
+class NumericFeature<int value> : AMDGPUGenericAnyFeature<
+    "any-b-"#value, "TestProperty", !cast<string>(value), "">;
+def FeatureAnySmall : NumericFeature<4>;
+def FeatureAnyB : NumericFeature<8>;
+def FeatureAnyLarge : NumericFeature<16>;
+def FeaturePrivate : AMDGPUGenericAnyFeature<"private", "HasPrivate", "true", "">;
+def FeatureImpliesA : SubtargetFeature<
+    "implies-a", "HasImpliesA", "true", "", [FeatureAnyA]>;
+def FeatureImpliesB : SubtargetFeature<
+    "implies-b", "HasImpliesB", "true", "", [FeatureAnyB]>;
+def FeatureAnyParent : AMDGPUGenericAnyFeature<
+    "any-parent", "HasAnyParent", "true", "", [FeatureAll], InlineIgnore>;
+
+def AMDGPUFrontendVisibleFeatures {
+  list<SubtargetFeature> Features = [FeatureAll, FeatureAnyA, FeatureAnyB,
+                                   FeatureAnyParent];
+}
+
+//--- valid.td
+include "common.td"
+def GFX900 : AMDGPUProcessorModel<"gfx900", NoSchedModel,
+    [FeatureAll, FeatureAnyA, FeatureAnySmall], [9, 0, 0]>;
+def GFX901 : AMDGPUProcessorModel<"gfx901", NoSchedModel,
+    [FeatureAll, FeatureImpliesB, FeaturePrivate], [9, 0, 1]>;
+// Different members supply different marked features. Follow Implies on both
+// the generic and the members, and check marked backend-only features too.
+def : AMDGPUProcessorModel<"gfx9-generic", NoSchedModel,
+    [FeatureAll, FeatureImpliesA, FeatureAnyB, FeaturePrivate], [9, 0, 0]> {
+  let CoveredGPUs = [GFX900, GFX901];
+}
+// Validation does not change which features are emitted in the frontend bitset.
+// CHECK: Triple::AMDGPUSubArch9, AMDGPUFeatureBitset({FEAT_ALL, FEAT_ANY_A, FEAT_ANY_B_8})
+
+//--- missing-any.td
+include "common.td"
+def GFX900 : AMDGPUProcessorModel<"gfx900", NoSchedModel, [], [9, 0, 0]>;
+def GFX901 : AMDGPUProcessorModel<"gfx901", NoSchedModel, [], [9, 0, 1]>;
+// CHECK: [[FILE]]:[[#@LINE+1]]:1: error: generic target 'gfx9-generic' exposes feature 'any-a' not supported by any covered GPU
+def : AMDGPUProcessorModel<"gfx9-generic", NoSchedModel, [FeatureAnyA], [9, 0, 0]> {
+  let CoveredGPUs = [GFX900, GFX901];
+}
+
+//--- missing-all.td
+include "common.td"
+def GFX900 : AMDGPUProcessorModel<"gfx900", NoSchedModel, [FeatureAll], [9, 0, 0]>;
+def GFX901 : AMDGPUProcessorModel<"gfx901", NoSchedModel, [], [9, 0, 1]>;
+// Ordinary features still require support from every member.
+// CHECK: [[FILE]]:[[#@LINE+1]]:1: error: generic target 'gfx9-generic' exposes feature 'all' not supported by covered GPU 'gfx901'
+def : AMDGPUProcessorModel<"gfx9-generic", NoSchedModel, [FeatureAll], [9, 0, 0]> {
+  let CoveredGPUs = [GFX900, GFX901];
+}
+
+//--- implied-all.td
+include "common.td"
+def GFX900 : AMDGPUProcessorModel<"gfx900", NoSchedModel, [FeatureAnyParent], [9, 0, 0]>;
+def GFX901 : AMDGPUProcessorModel<"gfx901", NoSchedModel, [], [9, 0, 1]>;
+// The parent's relaxed rule does not apply to its ordinary implied feature.
+// CHECK: [[FILE]]:[[#@LINE+1]]:1: error: generic target 'gfx9-generic' exposes feature 'all' not supported by covered GPU 'gfx901'
+def : AMDGPUProcessorModel<"gfx9-generic", NoSchedModel, [FeatureAnyParent], [9, 0, 0]> {
+  let CoveredGPUs = [GFX900, GFX901];
+}
+
+//--- missing-private.td
+include "common.td"
+def GFX900 : AMDGPUProcessorModel<"gfx900", NoSchedModel, [], [9, 0, 0]>;
+// Explicitly marked features are checked even outside the frontend-visible set.
+// CHECK: [[FILE]]:[[#@LINE+1]]:1: error: generic target 'gfx9-generic' exposes feature 'private' not supported by any covered GPU
+def : AMDGPUProcessorModel<"gfx9-generic", NoSchedModel, [FeaturePrivate], [9, 0, 0]> {
+  let CoveredGPUs = [GFX900];
+}
+
+//--- numeric-mismatch.td
+include "common.td"
+def GFX900 : AMDGPUProcessorModel<"gfx900", NoSchedModel, [FeatureAnySmall], [9, 0, 0]>;
+def GFX901 : AMDGPUProcessorModel<"gfx901", NoSchedModel, [FeatureAnyLarge], [9, 0, 1]>;
+// Members provide 4 and 16, but neither provides the generic's exact feature 8.
+// CHECK: [[FILE]]:[[#@LINE+1]]:1: error: generic target 'gfx9-generic' exposes feature 'any-b-8' not supported by any covered GPU
+def : AMDGPUProcessorModel<"gfx9-generic", NoSchedModel, [FeatureAnyB], [9, 0, 0]> {
+  let CoveredGPUs = [GFX900, GFX901];
+}
+
+//--- empty-members.td
+include "common.td"
+// A named generic target must not bypass validation by leaving its list empty.
+// CHECK: [[FILE]]:[[#@LINE+1]]:1: error: generic target 'gfx9-generic' must cover at least one GPU
+def : AMDGPUProcessorModel<"gfx9-generic", NoSchedModel, [FeatureAnyA], [9, 0, 0]>;
+
+//--- self-member.td
+include "common.td"
+// A generic target cannot supply its own feature support.
+// CHECK: [[FILE]]:[[#@LINE+1]]:5: error: generic target 'gfx9-generic' covers 'gfx9-generic', which is not a concrete AMDGPU GPU
+def Self : AMDGPUProcessorModel<"gfx9-generic", NoSchedModel, [FeatureAnyA], [9, 0, 0]> {
+  let CoveredGPUs = [Self];
+}
+
+//--- generic-member.td
+include "common.td"
+def GFX910 : AMDGPUProcessorModel<"gfx910", NoSchedModel, [FeatureAnyA], [9, 1, 0]>;
+def GFX9_1_GENERIC : AMDGPUProcessorModel<"gfx9-1-generic", NoSchedModel, [FeatureAnyA], [9, 1, 0]> {
+  let CoveredGPUs = [GFX910];
+}
+// Covered GPUs must be concrete even when another generic has valid members.
+// CHECK: [[FILE]]:[[#@LINE+1]]:1: error: generic target 'gfx9-generic' covers 'gfx9-1-generic', which is not a concrete AMDGPU GPU
+def : AMDGPUProcessorModel<"gfx9-generic", NoSchedModel, [FeatureAnyA], [9, 0, 0]> {
+  let CoveredGPUs = [GFX9_1_GENERIC];
+}
+
+//--- pseudo-member.td
+include "common.td"
+def Pseudo : AMDGPUProcessorModel<"generic", NoSchedModel, [FeatureAnyA], [9, 0, 0]> {
+  let IsPseudoTarget = true;
+}
+// CHECK: [[FILE]]:[[#@LINE+1]]:1: error: generic target 'gfx9-generic' covers 'generic', which is not a concrete AMDGPU GPU
+def : AMDGPUProcessorModel<"gfx9-generic", NoSchedModel, [FeatureAnyA], [9, 0, 0]> {
+  let CoveredGPUs = [Pseudo];
+}
+
+//--- unregistered-member.td
+include "common.td"
+def Unregistered : ProcessorModel<"gfx900", NoSchedModel, [FeatureAnyA]>;
+// CHECK: [[FILE]]:[[#@LINE+1]]:1: error: generic target 'gfx9-generic' covers 'gfx900', which is not a concrete AMDGPU GPU
+def : AMDGPUProcessorModel<"gfx9-generic", NoSchedModel, [FeatureAnyA], [9, 0, 0]> {
+  let CoveredGPUs = [Unregistered];
+}
+
+//--- r600-member.td
+include "common.td"
+def Cayman : R600ProcessorModel<"cayman", NoItineraries, [FeatureAnyA]>;
+// CHECK: [[FILE]]:[[#@LINE+1]]:1: error: generic target 'gfx9-generic' covers 'cayman', which is not a concrete AMDGPU GPU
+def : AMDGPUProcessorModel<"gfx9-generic", NoSchedModel, [FeatureAnyA], [9, 0, 0]> {
+  let CoveredGPUs = [Cayman];
+}
diff --git a/llvm/utils/TableGen/Basic/AMDGPUTargetDefEmitter.cpp b/llvm/utils/TableGen/Basic/AMDGPUTargetDefEmitter.cpp
index 69da1d25896fb..ba57831333334 100644
--- a/llvm/utils/TableGen/Basic/AMDGPUTargetDefEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/AMDGPUTargetDefEmitter.cpp
@@ -461,44 +461,57 @@ static void emitFeatureNames(raw_ostream &OS, const FeatureNaming &Naming,
      << "#endif // " << Naming.NameTableGuard << "\n\n";
 }
 
-// The set of frontend features that end up in the emitted bitset.
+// Features checked for generic-target compatibility: frontend-visible features
+// and features explicitly opting into the any-covered-GPU rule.
 static SetVector<const Record *>
-collectVisibleFeatures(const Record *GPU,
+collectGenericFeatures(const Record *GPU,
                        const DenseMap<const Record *, unsigned> &FeatureIdx) {
   SetVector<const Record *> Closure;
   collectFeatureClosure(GPU, Closure);
-  SetVector<const Record *> Visible;
+  SetVector<const Record *> Features;
   for (const Record *F : Closure) {
-    if (FeatureIdx.contains(F))
-      Visible.insert(F);
+    if (FeatureIdx.contains(F) || F->isSubClassOf("AMDGPUGenericAnyFeature"))
+      Features.insert(F);
   }
 
-  return Visible;
+  return Features;
 }
 
-// Make sure a "gfxN-generic" processor doesn't expose a frontend-visible
-// feature missing from any covered processor.
+// Ordinary frontend-visible features must be present on every covered GPU.
+// AMDGPUGenericAnyFeature features need only be present on one covered GPU.
 //
-// FIXME: The check should cover all SubtargetFeatures, not just the
-// frontend-visible ones. It is limited to those because a generic legitimately
-// carries some features a covered GPU lacks (bug/hazard workarounds and
-// worst-case-valued features); those cases need to be marked to opt out of the
-// check, plus min-value handling for numeric features.
+// FIXME: Classify the remaining backend-only features before checking them.
 static void
 validateGenericFeatures(const Record *GPU,
                         const DenseMap<const Record *, unsigned> &FeatureIdx) {
+  StringRef Name = GPU->getValueAsString("Name");
   std::vector<const Record *> Covered =
       GPU->getValueAsListOfDefs("CoveredGPUs");
-  if (Covered.empty())
+  if (Covered.empty()) {
+    if (Name.starts_with("gfx") && Name.ends_with("-generic"))
+      PrintFatalError(GPU->getLoc(), "generic target '" + Name +
+                                         "' must cover at least one GPU");
     return;
+  }
 
   SetVector<const Record *> GenericFeatures =
-      collectVisibleFeatures(GPU, FeatureIdx);
+      collectGenericFeatures(GPU, FeatureIdx);
+  SetVector<const Record *> CoveredFeatures;
   for (const Record *Member : Covered) {
+    if (!Member->isSubClassOf("AMDGPUGPUInfo") ||
+        !Member->isSubClassOf("ProcessorModel") ||
+        Member->getValueAsBit("IsPseudoTarget") || isGenericTarget(Member))
+      PrintFatalError(GPU->getLoc(),
+                      "generic target '" + Name + "' covers '" +
+                          Member->getValueAsString("Name") +
+                          "', which is not a concrete AMDGPU GPU");
+
     SetVector<const Record *> MemberFeatures =
-        collectVisibleFeatures(Member, FeatureIdx);
+        collectGenericFeatures(Member, FeatureIdx);
+    CoveredFeatures.insert_range(MemberFeatures);
     for (const Record *F : GenericFeatures) {
-      if (!MemberFeatures.contains(F)) {
+      if (!F->isSubClassOf("AMDGPUGenericAnyFeature") &&
+          !MemberFeatures.contains(F)) {
         PrintFatalError(GPU->getLoc(),
                         "generic target '" + GPU->getValueAsString("Name") +
                             "' exposes feature '" +
@@ -508,6 +521,14 @@ validateGenericFeatures(const Record *GPU,
       }
     }
   }
+
+  for (const Record *F : GenericFeatures) {
+    if (!CoveredFeatures.contains(F))
+      PrintFatalError(GPU->getLoc(),
+                      "generic target '" + GPU->getValueAsString("Name") +
+                          "' exposes feature '" + F->getValueAsString("Name") +
+                          "' not supported by any covered GPU");
+  }
 }
 
 static void validateAMDGPU(const RecordKeeper &RK) {



More information about the llvm-commits mailing list