[clang] 93da31b - [RFC][CodeGen] Add generic target feature checks for intrinsics (#201470)

via cfe-commits cfe-commits at lists.llvm.org
Mon Jun 29 19:41:20 PDT 2026


Author: Shilei Tian
Date: 2026-06-29T22:41:15-04:00
New Revision: 93da31b66e5a65c3ee55bfa9ef0f7e82d1822a1b

URL: https://github.com/llvm/llvm-project/commit/93da31b66e5a65c3ee55bfa9ef0f7e82d1822a1b
DIFF: https://github.com/llvm/llvm-project/commit/93da31b66e5a65c3ee55bfa9ef0f7e82d1822a1b.diff

LOG: [RFC][CodeGen] Add generic target feature checks for intrinsics (#201470)

This PR adds target-independent infrastructure for annotating LLVM
intrinsics with required subtarget feature expressions.

It introduces a TargetFeatures string field to intrinsic TableGen
records. TableGen emits an intrinsic-to-feature mapping table.

Both SelectionDAG and GlobalISel now perform this check before lowering
target intrinsics. This allows targets to opt in by annotating intrinsic
definitions directly, rather than adding custom checks during lowering,
legalization, or instruction selection.

This PR uses one AMDGPU intrinsic as an example.

Added: 
    llvm/test/TableGen/intrinsic-target-features.td

Modified: 
    clang/lib/CodeGen/BackendConsumer.h
    clang/lib/CodeGen/CodeGenAction.cpp
    llvm/docs/ReleaseNotes.md
    llvm/include/llvm/CodeGen/TargetSubtargetInfo.h
    llvm/include/llvm/IR/DiagnosticInfo.h
    llvm/include/llvm/IR/Intrinsics.h
    llvm/include/llvm/IR/Intrinsics.td
    llvm/include/llvm/IR/IntrinsicsAMDGPU.td
    llvm/include/llvm/MC/MCSubtargetInfo.h
    llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
    llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
    llvm/lib/CodeGen/TargetSubtargetInfo.cpp
    llvm/lib/IR/DiagnosticInfo.cpp
    llvm/lib/IR/Intrinsics.cpp
    llvm/lib/MC/MCSubtargetInfo.cpp
    llvm/test/CodeGen/AMDGPU/llvm.amdgcn.permlane16.swap.ll
    llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
    llvm/utils/TableGen/Basic/CodeGenIntrinsics.h
    llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/BackendConsumer.h b/clang/lib/CodeGen/BackendConsumer.h
index eeac13bd42379..708658d206baf 100644
--- a/clang/lib/CodeGen/BackendConsumer.h
+++ b/clang/lib/CodeGen/BackendConsumer.h
@@ -120,6 +120,9 @@ class BackendConsumer : public ASTConsumer {
 
   /// Specialized handler for unsupported backend feature diagnostic.
   void UnsupportedDiagHandler(const llvm::DiagnosticInfoUnsupported &D);
+  /// Specialized handler for unsupported target intrinsic diagnostic.
+  void UnsupportedTargetIntrinsicDiagHandler(
+      const llvm::DiagnosticInfoUnsupportedTargetIntrinsic &D);
   /// Specialized handlers for optimization remarks.
   /// Note that these handlers only accept remarks and they always handle
   /// them.

diff  --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp
index 1371fd48cb524..6911cab379fdc 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -631,6 +631,40 @@ void BackendConsumer::UnsupportedDiagHandler(
         << Filename << Line << Column;
 }
 
+void BackendConsumer::UnsupportedTargetIntrinsicDiagHandler(
+    const llvm::DiagnosticInfoUnsupportedTargetIntrinsic &D) {
+  assert(D.getSeverity() == llvm::DS_Error &&
+         "unsupported target intrinsic diagnostic should be an error");
+
+  StringRef Filename;
+  unsigned Line, Column;
+  bool BadDebugInfo = false;
+  FullSourceLoc Loc;
+  std::string Msg;
+  raw_string_ostream MsgStream(Msg);
+
+  // Context will be nullptr for IR input files, so construct the diagnostic
+  // message from llvm::DiagnosticInfoUnsupportedTargetIntrinsic.
+  if (Context != nullptr) {
+    Loc = getBestLocationFromDebugLoc(D, BadDebugInfo, Filename, Line, Column);
+    MsgStream << D.getMessage();
+  } else {
+    DiagnosticPrinterRawOStream DP(MsgStream);
+    D.print(DP);
+  }
+
+  Diags.Report(Loc, diag::err_fe_backend_unsupported) << Msg;
+
+  if (BadDebugInfo) {
+    // If we were not able to translate the file:line:col information
+    // back to a SourceLocation, at least emit a note stating that
+    // we could not translate this location. This can happen in the
+    // case of #line directives.
+    Diags.Report(Loc, diag::note_fe_backend_invalid_loc)
+        << Filename << Line << Column;
+  }
+}
+
 void BackendConsumer::EmitOptimizationMessage(
     const llvm::DiagnosticInfoOptimizationBase &D, unsigned DiagID) {
   // We only support warnings and remarks.
@@ -880,6 +914,10 @@ void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) {
   case llvm::DK_Unsupported:
     UnsupportedDiagHandler(cast<DiagnosticInfoUnsupported>(DI));
     return;
+  case llvm::DK_UnsupportedTargetIntrinsic:
+    UnsupportedTargetIntrinsicDiagHandler(
+        cast<DiagnosticInfoUnsupportedTargetIntrinsic>(DI));
+    return;
   case llvm::DK_DontCall:
     DontCallDiagHandler(cast<DiagnosticInfoDontCall>(DI));
     return;

diff  --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index d1448eb469614..143c6b4113e78 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -319,6 +319,11 @@ Makes programs 10x faster by doing Special New Thing.
 * Renamed ISD::CTTZ_ZERO_UNDEF to ISD::CTTZ_ZERO_POISON opcode to make it clear that
   a zero input results in poison.
 
+* LLVM intrinsics can now declare required target features using the
+  ``TargetFeatures`` TableGen field. SelectionDAG and GlobalISel use this
+  metadata to reject unsupported target intrinsics generically, so targets do
+  not need custom lowering checks for each annotated intrinsic.
+
 ### Changes to the GlobalISel infrastructure
 
 * Renamed G_CTLZ_ZERO_UNDEF to G_CTLZ_ZERO_POISON opcode to make it clear that

diff  --git a/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h b/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h
index fe71a17146184..54b92a5ac0deb 100644
--- a/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h
+++ b/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h
@@ -14,6 +14,7 @@
 #define LLVM_CODEGEN_TARGETSUBTARGETINFO_H
 
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/CodeGen/MacroFusion.h"
@@ -87,6 +88,10 @@ class LLVM_ABI TargetSubtargetInfo : public MCSubtargetInfo {
 
   virtual bool isXRaySupported() const { return false; }
 
+  /// \returns true if the target intrinsic \p IntrinsicID is supported by this
+  /// subtarget.
+  bool isIntrinsicSupported(unsigned IntrinsicID) const;
+
   // Interfaces to the major aspects of target machine information:
   //
   // -- Instruction opcode and operand information
@@ -173,7 +178,7 @@ class LLVM_ABI TargetSubtargetInfo : public MCSubtargetInfo {
   ///
   /// Similar in behavior to `isZeroIdiom`. However, it knows how to identify
   /// all dependency breaking instructions (i.e. not just zero-idioms).
-  /// 
+  ///
   /// As for `isZeroIdiom`, this method returns a mask of "broken" dependencies.
   /// (See method `isZeroIdiom` for a detailed description of Mask).
   virtual bool isDependencyBreaking(const MachineInstr *MI, APInt &Mask) const {
@@ -370,6 +375,10 @@ class LLVM_ABI TargetSubtargetInfo : public MCSubtargetInfo {
   /// Target features where the callee may have an additional feature,
   /// instead of the caller.
   virtual const FeatureBitset &getInlineInverseFeatures() const = 0;
+
+private:
+  /// Lazy, incrementally-populated cache for isIntrinsicSupported().
+  mutable DenseMap<unsigned, bool> IntrinsicSupportCache;
 };
 } // end namespace llvm
 

diff  --git a/llvm/include/llvm/IR/DiagnosticInfo.h b/llvm/include/llvm/IR/DiagnosticInfo.h
index aff4c81ad2856..da62b62bd8c74 100644
--- a/llvm/include/llvm/IR/DiagnosticInfo.h
+++ b/llvm/include/llvm/IR/DiagnosticInfo.h
@@ -89,6 +89,7 @@ enum DiagnosticKind {
   DK_MIRParser,
   DK_PGOProfile,
   DK_Unsupported,
+  DK_UnsupportedTargetIntrinsic,
   DK_SrcMgr,
   DK_DontCall,
   DK_MisExpect,
@@ -1127,6 +1128,29 @@ class LLVM_ABI DiagnosticInfoUnsupported
   void print(DiagnosticPrinter &DP) const override;
 };
 
+/// Diagnostic information for unsupported target intrinsics in backend.
+class LLVM_ABI DiagnosticInfoUnsupportedTargetIntrinsic
+    : public DiagnosticInfoWithLocationBase {
+private:
+  unsigned IntrinsicID;
+  StringRef RequiredFeatures;
+
+public:
+  DiagnosticInfoUnsupportedTargetIntrinsic(
+      const Function &Fn, unsigned IntrinsicID,
+      const DiagnosticLocation &Loc = DiagnosticLocation());
+
+  static bool classof(const DiagnosticInfo *DI) {
+    return DI->getKind() == DK_UnsupportedTargetIntrinsic;
+  }
+
+  unsigned getIntrinsicID() const { return IntrinsicID; }
+  StringRef getRequiredFeatures() const { return RequiredFeatures; }
+  std::string getMessage() const;
+
+  void print(DiagnosticPrinter &DP) const override;
+};
+
 /// Diagnostic information for MisExpect analysis.
 class LLVM_ABI DiagnosticInfoMisExpect : public DiagnosticInfoWithLocationBase {
 public:

diff  --git a/llvm/include/llvm/IR/Intrinsics.h b/llvm/include/llvm/IR/Intrinsics.h
index 5ef2e33470634..76269fbcb1412 100644
--- a/llvm/include/llvm/IR/Intrinsics.h
+++ b/llvm/include/llvm/IR/Intrinsics.h
@@ -16,6 +16,7 @@
 #define LLVM_IR_INTRINSICS_H
 
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/TypeSize.h"
 #include <optional>
@@ -62,6 +63,9 @@ LLVM_ABI StringRef getName(ID id);
 /// overloading, such as "llvm.ssa.copy".
 LLVM_ABI StringRef getBaseName(ID id);
 
+/// \returns the target feature expression required by an intrinsic.
+LLVM_ABI StringRef getRequiredTargetFeatures(ID id);
+
 /// Return the LLVM name for an intrinsic, such as "llvm.ppc.altivec.lvx" or
 /// "llvm.ssa.copy.p0s_s.1". Note, this version of getName supports overloads.
 /// This is less efficient than the StringRef version of this function.  If no

diff  --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 129fe08ce3877..007882c492b4c 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -763,6 +763,11 @@ class TypeInfoGen<list<LLVMType> RetTypes, list<LLVMType> ParamTypes> {
 //  * ParamTypes is a list containing the parameter types expected for the
 //    intrinsic.
 //  * Properties can be set to describe the behavior of the intrinsic.
+//  * TargetFeatures is a target feature expression required by the intrinsic.
+//    The empty string means no target features are required. The expression
+//    uses feature names from the target's subtarget feature table. Comma means
+//    AND, | means OR, comma has higher precedence than |, and parentheses group
+//    expressions.
 //
 class Intrinsic<list<LLVMType> ret_types,
                 list<LLVMType> param_types = [],
@@ -772,6 +777,7 @@ class Intrinsic<list<LLVMType> ret_types,
                 bit disable_default_attributes = true> : SDPatternOperator {
   string LLVMName = name;
   string TargetPrefix = "";   // Set to a prefix for target-specific intrinsics.
+  string TargetFeatures = ""; // Target features required by this intrinsic.
   list<LLVMType> RetTypes = ret_types;
   list<LLVMType> ParamTypes = param_types;
   list<IntrinsicProperty> IntrProperties = intr_properties;
@@ -805,6 +811,15 @@ class MSBuiltin<string name> {
   string MSBuiltinName = name;
 }
 
+/// RequiresTargetFeatures - If this intrinsic requires target features,
+/// this specifies the required feature expression using feature names from the
+/// target's subtarget feature table. The expression grammar matches Clang
+/// builtins: comma means AND, | means OR, comma has higher precedence than |,
+/// and parentheses group expressions.
+class RequiresTargetFeatures<string features> {
+  string TargetFeatures = features;
+}
+
 /// Utility class for intrinsics that
 /// 1. Don't touch memory or any hidden state
 /// 2. Can be freely speculated, and

diff  --git a/llvm/include/llvm/IR/IntrinsicsAMDGPU.td b/llvm/include/llvm/IR/IntrinsicsAMDGPU.td
index 95dc490bf398a..21882e247c027 100644
--- a/llvm/include/llvm/IR/IntrinsicsAMDGPU.td
+++ b/llvm/include/llvm/IR/IntrinsicsAMDGPU.td
@@ -3725,6 +3725,7 @@ def int_amdgcn_smfmac_f32_32x32x64_fp8_fp8 : AMDGPUMSmfmacIntrinsic<llvm_v16f32_
 }
 
 // { vdst_new, vsrc_new } llvm.amdgcn.permlane16.swap <vdst_old> <vsrc_old> <fi> <bound_control>
+let TargetFeatures = "permlane16-swap" in
 def int_amdgcn_permlane16_swap :
   Intrinsic<[llvm_i32_ty, llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty,
                                          llvm_i1_ty, llvm_i1_ty],

diff  --git a/llvm/include/llvm/MC/MCSubtargetInfo.h b/llvm/include/llvm/MC/MCSubtargetInfo.h
index 0393dea60b938..7f55ab7cd9d89 100644
--- a/llvm/include/llvm/MC/MCSubtargetInfo.h
+++ b/llvm/include/llvm/MC/MCSubtargetInfo.h
@@ -185,6 +185,12 @@ class LLVM_ABI MCSubtargetInfo {
   /// the provided string, ignoring all other features.
   bool checkFeatures(StringRef FS) const;
 
+  /// Check whether the current subtarget satisfies a target feature expression.
+  /// The expression uses feature names from the target's subtarget feature
+  /// table. Comma means AND, | means OR, comma has higher precedence than |,
+  /// and parentheses group expressions.
+  bool checkFeatureExpression(StringRef FeatureExpr) const;
+
   /// Get the machine model of a CPU.
   const MCSchedModel &getSchedModelForCPU(StringRef CPU) const;
 

diff  --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
index 7a8f2a8431f9b..8b792aaac77c3 100644
--- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
@@ -2877,6 +2877,13 @@ bool IRTranslator::translateCall(const User &U, MachineIRBuilder &MIRBuilder) {
 
   assert(ID != Intrinsic::not_intrinsic && "unknown intrinsic");
 
+  if (!MF->getSubtarget().isIntrinsicSupported(ID)) {
+    const Function &Fn = MF->getFunction();
+    Fn.getContext().diagnose(
+        DiagnosticInfoUnsupportedTargetIntrinsic(Fn, ID, CI.getDebugLoc()));
+    return false;
+  }
+
   if (translateKnownIntrinsic(CI, ID, MIRBuilder))
     return true;
 
@@ -2890,6 +2897,13 @@ bool IRTranslator::translateCall(const User &U, MachineIRBuilder &MIRBuilder) {
 bool IRTranslator::translateIntrinsic(
     const CallBase &CB, Intrinsic::ID ID, MachineIRBuilder &MIRBuilder,
     ArrayRef<TargetLowering::IntrinsicInfo> TgtMemIntrinsicInfos) {
+  if (!MF->getSubtarget().isIntrinsicSupported(ID)) {
+    const Function &F = MF->getFunction();
+    F.getContext().diagnose(
+        DiagnosticInfoUnsupportedTargetIntrinsic(F, ID, CB.getDebugLoc()));
+    return false;
+  }
+
   ArrayRef<Register> ResultRegs;
   if (!CB.getType()->isVoidTy())
     ResultRegs = getOrCreateVRegs(CB);

diff  --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 8aa184423c60c..f5a4e891ce133 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -5527,6 +5527,31 @@ SDValue SelectionDAGBuilder::handleTargetIntrinsicRet(const CallBase &I,
 void SelectionDAGBuilder::visitTargetIntrinsic(const CallInst &I,
                                                unsigned Intrinsic) {
   auto [HasChain, OnlyLoad] = getTargetIntrinsicCallProperties(I);
+  Intrinsic::ID IntrinsicID = static_cast<Intrinsic::ID>(Intrinsic);
+
+  if (!DAG.getMachineFunction().getSubtarget().isIntrinsicSupported(
+          Intrinsic)) {
+    SDLoc DL = getCurSDLoc();
+    DAG.getContext()->diagnose(DiagnosticInfoUnsupportedTargetIntrinsic(
+        *I.getFunction(), IntrinsicID, DL.getDebugLoc()));
+
+    // The intrinsic is not available on this subtarget. Preserve the chain for
+    // side-effecting intrinsics and lower any result to poison so that
+    // compilation can continue and collect further diagnostics.
+    if (HasChain && !OnlyLoad)
+      DAG.setRoot(getRoot());
+
+    if (!I.getType()->isVoidTy()) {
+      SmallVector<EVT, 4> ValueVTs;
+      ComputeValueVTs(DAG.getTargetLoweringInfo(), DAG.getDataLayout(),
+                      I.getType(), ValueVTs);
+      SmallVector<SDValue, 4> Results;
+      for (EVT VT : ValueVTs)
+        Results.push_back(DAG.getPOISON(VT));
+      setValue(&I, DAG.getMergeValues(Results, DL));
+    }
+    return;
+  }
 
   // Infos is set by getTgtMemIntrinsic.
   SmallVector<TargetLowering::IntrinsicInfo> Infos;

diff  --git a/llvm/lib/CodeGen/TargetSubtargetInfo.cpp b/llvm/lib/CodeGen/TargetSubtargetInfo.cpp
index cd396e6a619a8..f85d456122780 100644
--- a/llvm/lib/CodeGen/TargetSubtargetInfo.cpp
+++ b/llvm/lib/CodeGen/TargetSubtargetInfo.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/CodeGen/TargetSubtargetInfo.h"
+#include "llvm/IR/Intrinsics.h"
 
 using namespace llvm;
 
@@ -25,6 +26,19 @@ TargetSubtargetInfo::TargetSubtargetInfo(
 
 TargetSubtargetInfo::~TargetSubtargetInfo() = default;
 
+bool TargetSubtargetInfo::isIntrinsicSupported(unsigned IntrinsicID) const {
+  StringRef RequiredFeatures = Intrinsic::getRequiredTargetFeatures(
+      static_cast<Intrinsic::ID>(IntrinsicID));
+
+  if (RequiredFeatures.empty())
+    return true;
+
+  auto [It, Inserted] = IntrinsicSupportCache.try_emplace(IntrinsicID);
+  if (Inserted)
+    It->second = checkFeatureExpression(RequiredFeatures);
+  return It->second;
+}
+
 bool TargetSubtargetInfo::enableAtomicExpand() const {
   return true;
 }

diff  --git a/llvm/lib/IR/DiagnosticInfo.cpp b/llvm/lib/IR/DiagnosticInfo.cpp
index caa7d23e772d5..a24b6d0935008 100644
--- a/llvm/lib/IR/DiagnosticInfo.cpp
+++ b/llvm/lib/IR/DiagnosticInfo.cpp
@@ -26,6 +26,7 @@
 #include "llvm/IR/Instruction.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/IR/Module.h"
@@ -418,6 +419,36 @@ void DiagnosticInfoUnsupported::print(DiagnosticPrinter &DP) const {
   DP << Str;
 }
 
+DiagnosticInfoUnsupportedTargetIntrinsic::
+    DiagnosticInfoUnsupportedTargetIntrinsic(const Function &Fn,
+                                             unsigned IntrinsicID,
+                                             const DiagnosticLocation &Loc)
+    : DiagnosticInfoWithLocationBase(DK_UnsupportedTargetIntrinsic, DS_Error,
+                                     Fn, Loc),
+      IntrinsicID(IntrinsicID),
+      RequiredFeatures(Intrinsic::getRequiredTargetFeatures(
+          static_cast<Intrinsic::ID>(IntrinsicID))) {
+  assert(!RequiredFeatures.empty() &&
+         "intrinsic without required features should be supported");
+}
+
+std::string DiagnosticInfoUnsupportedTargetIntrinsic::getMessage() const {
+  return (Twine(
+              Intrinsic::getBaseName(static_cast<Intrinsic::ID>(IntrinsicID))) +
+          " requires target feature '" + RequiredFeatures + "'")
+      .str();
+}
+
+void DiagnosticInfoUnsupportedTargetIntrinsic::print(
+    DiagnosticPrinter &DP) const {
+  std::string Str;
+  raw_string_ostream OS(Str);
+  OS << getLocationStr() << ": in function ";
+  getFunction().printAsOperand(OS, /*PrintType=*/false);
+  OS << ' ' << *getFunction().getFunctionType() << ": " << getMessage() << '\n';
+  DP << Str;
+}
+
 void DiagnosticInfoInstrumentation::print(DiagnosticPrinter &DP) const {
   DP << Msg;
 }

diff  --git a/llvm/lib/IR/Intrinsics.cpp b/llvm/lib/IR/Intrinsics.cpp
index eb9690f066251..044ee61b10a26 100644
--- a/llvm/lib/IR/Intrinsics.cpp
+++ b/llvm/lib/IR/Intrinsics.cpp
@@ -50,11 +50,20 @@ static bool isSignatureValid(FunctionType *FTy,
 #define GET_INTRINSIC_NAME_TABLE
 #include "llvm/IR/IntrinsicImpl.inc"
 
+/// Table of required target features indexed by enum value.
+#define GET_INTRINSIC_TARGET_FEATURES_TABLE
+#include "llvm/IR/IntrinsicImpl.inc"
+
 StringRef Intrinsic::getBaseName(ID id) {
   assert(id < num_intrinsics && "Invalid intrinsic ID!");
   return IntrinsicNameTable[IntrinsicNameOffsetTable[id]];
 }
 
+StringRef Intrinsic::getRequiredTargetFeatures(ID id) {
+  assert(id < num_intrinsics && "invalid intrinsic ID!");
+  return IntrinsicTargetFeaturesTable[IntrinsicTargetFeaturesOffsetTable[id]];
+}
+
 StringRef Intrinsic::getName(ID id) {
   assert(id < num_intrinsics && "Invalid intrinsic ID!");
   assert(!Intrinsic::isOverloaded(id) &&

diff  --git a/llvm/lib/MC/MCSubtargetInfo.cpp b/llvm/lib/MC/MCSubtargetInfo.cpp
index 2cae4643641a3..013a2330a96d1 100644
--- a/llvm/lib/MC/MCSubtargetInfo.cpp
+++ b/llvm/lib/MC/MCSubtargetInfo.cpp
@@ -333,15 +333,108 @@ bool MCSubtargetInfo::checkFeatures(StringRef FS) const {
            "Feature flags should start with '+' or '-'");
     const SubtargetFeatureKV *FeatureEntry =
         Find(SubtargetFeatures::StripFlag(F), ProcFeatures);
-    if (!FeatureEntry)
-      report_fatal_error(Twine("'") + F +
-                         "' is not a recognized feature for this target");
+    if (!FeatureEntry) {
+      reportFatalInternalError(Twine("'") + F +
+                               "' is not a recognized feature for this target");
+    }
 
     return FeatureBits.test(FeatureEntry->Value) ==
            SubtargetFeatures::isEnabled(F);
   });
 }
 
+static bool hasFeature(StringRef Feature, const FeatureBitset &FeatureBits,
+                       ArrayRef<SubtargetFeatureKV> ProcFeatures) {
+  bool ShouldBeEnabled = true;
+  if (!Feature.consume_front("+") && Feature.consume_front("-"))
+    ShouldBeEnabled = false;
+
+  const SubtargetFeatureKV *FeatureEntry = Find(Feature, ProcFeatures);
+  if (!FeatureEntry) {
+    reportFatalInternalError(Twine("'") + Feature +
+                             "' is not a recognized feature for this target");
+  }
+
+  return FeatureBits.test(FeatureEntry->Value) == ShouldBeEnabled;
+}
+
+namespace {
+class FeatureExpressionParser {
+  StringRef Expr;
+  const FeatureBitset &FeatureBits;
+  ArrayRef<SubtargetFeatureKV> ProcFeatures;
+  size_t Pos = 0;
+
+public:
+  FeatureExpressionParser(StringRef Expr, const FeatureBitset &FeatureBits,
+                          ArrayRef<SubtargetFeatureKV> ProcFeatures)
+      : Expr(Expr), FeatureBits(FeatureBits), ProcFeatures(ProcFeatures) {}
+
+  bool parse() {
+    bool Result = parseOr();
+    if (Pos != Expr.size())
+      reportFatalInternalError("malformed target feature expression");
+    return Result;
+  }
+
+private:
+  bool consume(char C) {
+    if (Pos == Expr.size() || Expr[Pos] != C)
+      return false;
+    ++Pos;
+    return true;
+  }
+
+  bool parseOr() {
+    bool Result = parseAnd();
+    while (consume('|')) {
+      bool RHS = parseAnd();
+      Result |= RHS;
+    }
+    return Result;
+  }
+
+  bool parseAnd() {
+    bool Result = parsePrimary();
+    while (consume(',')) {
+      bool RHS = parsePrimary();
+      Result &= RHS;
+    }
+    return Result;
+  }
+
+  bool parsePrimary() {
+    if (consume('(')) {
+      bool Result = parseOr();
+      if (!consume(')'))
+        reportFatalInternalError("malformed target feature expression");
+      return Result;
+    }
+
+    size_t Start = Pos;
+    Pos = Expr.find_first_of(",|()", Pos);
+    if (Pos == StringRef::npos)
+      Pos = Expr.size();
+
+    if (Start == Pos)
+      reportFatalInternalError("malformed target feature expression");
+
+    return hasFeature(Expr.slice(Start, Pos), FeatureBits, ProcFeatures);
+  }
+};
+} // namespace
+
+bool MCSubtargetInfo::checkFeatureExpression(StringRef FeatureExpr) const {
+  if (FeatureExpr.empty())
+    return true;
+  if (FeatureExpr.contains(' ')) {
+    reportFatalInternalError(
+        "spaces are not allowed in target feature expressions");
+  }
+  FeatureExpressionParser Parser(FeatureExpr, FeatureBits, ProcFeatures);
+  return Parser.parse();
+}
+
 const MCSchedModel &MCSubtargetInfo::getSchedModelForCPU(StringRef CPU) const {
   assert(llvm::is_sorted(ProcDesc) &&
          "Processor machine model table is not sorted");

diff  --git a/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.permlane16.swap.ll b/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.permlane16.swap.ll
index ed6a02b62ae9a..598b53d3440a8 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.permlane16.swap.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.permlane16.swap.ll
@@ -4,11 +4,10 @@
 ; RUN: llc -global-isel=0 -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1250 < %s | FileCheck -check-prefix=GFX1250 %s
 ; RUN: llc -global-isel=1 -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1250 < %s | FileCheck -check-prefix=GFX1250 %s
 
-; RUN: not --crash llc -global-isel=0 -mtriple=amdgcn-amd-amdhsa -mcpu=gfx942 -filetype=null %s 2>&1 | FileCheck -check-prefix=ERR-SDAG %s
-; RUN: not llc -global-isel=1 -mtriple=amdgcn-amd-amdhsa -mcpu=gfx942 -filetype=null %s 2>&1 | FileCheck -check-prefix=ERR-GISEL %s
+; RUN: not llc -global-isel=0 -mtriple=amdgcn-amd-amdhsa -mcpu=gfx942 -filetype=null %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -global-isel=1 -global-isel-abort=0 -mtriple=amdgcn-amd-amdhsa -mcpu=gfx942 -filetype=null %s 2>&1 | FileCheck -check-prefix=ERR %s
 
-; ERR-SDAG: LLVM ERROR: Cannot select: intrinsic %llvm.amdgcn.permlane16.swap
-; ERR-GISEL: LLVM ERROR: cannot select: %{{[0-9]+}}:vgpr_32(s32), %{{[0-9]+}}:vgpr_32(s32) = G_INTRINSIC_CONVERGENT intrinsic(@llvm.amdgcn.permlane16.swap)
+; ERR: error: {{.*}}: in function {{@?}}v_permlane16_swap_b32_vv{{.*}}: llvm.amdgcn.permlane16.swap requires target feature 'permlane16-swap'
 
 
 declare { i32, i32 } @llvm.amdgcn.permlane16.swap(i32, i32, i1 immarg, i1 immarg)

diff  --git a/llvm/test/TableGen/intrinsic-target-features.td b/llvm/test/TableGen/intrinsic-target-features.td
new file mode 100644
index 0000000000000..e43985cab1d06
--- /dev/null
+++ b/llvm/test/TableGen/intrinsic-target-features.td
@@ -0,0 +1,28 @@
+// RUN: llvm-tblgen -gen-intrinsic-impl -I %p/../../include -DTEST_INTRINSICS_SUPPRESS_DEFS %s | FileCheck %s
+
+include "llvm/IR/Intrinsics.td"
+
+def int_no_feature : Intrinsic<[llvm_i32_ty], [], [IntrNoMem]>;
+
+def int_requires_feature : Intrinsic<[llvm_i32_ty], [], [IntrNoMem]>,
+  RequiresTargetFeatures<"feat-a,(feat-b|feat-c)">;
+
+let TargetFeatures = "feat-let-a,feat-let-b" in
+def int_let_feature : Intrinsic<[llvm_i32_ty], [], [IntrNoMem]>;
+
+// CHECK:      #ifdef GET_INTRINSIC_TARGET_FEATURES_TABLE
+// CHECK-NEXT: #undef GET_INTRINSIC_TARGET_FEATURES_TABLE
+// CHECK:      // Intrinsic ID to required target features table.
+// CHECK:      static constexpr char IntrinsicTargetFeaturesTableStorage[] =
+// CHECK-NEXT: "\0"
+// CHECK-NEXT: "feat-let-a,feat-let-b\0"
+// CHECK-NEXT: "feat-a,(feat-b|feat-c)\0"
+// CHECK:      static constexpr llvm::StringTable
+// CHECK-NEXT: IntrinsicTargetFeaturesTable = IntrinsicTargetFeaturesTableStorage;
+// CHECK:      static constexpr unsigned IntrinsicTargetFeaturesOffsetTable[] = {
+// CHECK-NEXT:   0, // not_intrinsic
+// CHECK-NEXT:   {{[0-9]+}}, // llvm.let.feature
+// CHECK-NEXT:   0, // llvm.no.feature
+// CHECK-NEXT:   {{[0-9]+}}, // llvm.requires.feature
+// CHECK:      }; // IntrinsicTargetFeaturesOffsetTable
+// CHECK:      #endif // GET_INTRINSIC_TARGET_FEATURES_TABLE

diff  --git a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
index 36898ae014e3a..e11f6cb02d5d1 100644
--- a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
+++ b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
@@ -276,6 +276,7 @@ CodeGenIntrinsic::CodeGenIntrinsic(const Record *R,
       R->getValueAsOptionalString("ClangBuiltinName").value_or("");
   // Ignore a missing MSBuiltinName field.
   MSBuiltinName = R->getValueAsOptionalString("MSBuiltinName").value_or("");
+  TargetFeatures = R->getValueAsString("TargetFeatures");
 
   TargetPrefix = R->getValueAsString("TargetPrefix");
   Name = R->getValueAsString("LLVMName").str();

diff  --git a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.h b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.h
index 7d2d21382b319..8ae6f41aae254 100644
--- a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.h
+++ b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.h
@@ -39,6 +39,7 @@ struct CodeGenIntrinsic {
   StringRef ClangBuiltinName; // Name of the corresponding GCC builtin, or "".
   StringRef MSBuiltinName;    // Name of the corresponding MS builtin, or "".
   StringRef TargetPrefix;     // Target prefix, e.g. "ppc" for t-s intrinsics.
+  StringRef TargetFeatures;   // Target feature expression required, or "".
 
   /// This structure holds the return values and parameter values of an
   /// intrinsic. If the number of return values is > 1, then the intrinsic

diff  --git a/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp b/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
index 5ce2d1dbc815d..4cf88b22c5e14 100644
--- a/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
@@ -59,6 +59,8 @@ class IntrinsicEmitter {
   void EmitTargetInfo(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);
   void EmitIntrinsicToNameTable(const CodeGenIntrinsicTable &Ints,
                                 raw_ostream &OS);
+  void EmitIntrinsicToTargetFeaturesTable(const CodeGenIntrinsicTable &Ints,
+                                          raw_ostream &OS);
   void EmitIntrinsicToOverloadTable(const CodeGenIntrinsicTable &Ints,
                                     raw_ostream &OS);
   void EmitIntrinsicToScalarizableTable(const CodeGenIntrinsicTable &Ints,
@@ -111,6 +113,9 @@ void IntrinsicEmitter::run(raw_ostream &OS, bool Enums) {
     // Emit the intrinsic ID -> name table.
     EmitIntrinsicToNameTable(Ints, OS);
 
+    // Emit the intrinsic ID -> required target features table.
+    EmitIntrinsicToTargetFeaturesTable(Ints, OS);
+
     // Emit the intrinsic ID -> overload table.
     EmitIntrinsicToOverloadTable(Ints, OS);
 
@@ -315,6 +320,33 @@ static constexpr unsigned IntrinsicNameOffsetTable[] = {
   OS << "\n}; // IntrinsicNameOffsetTable\n";
 }
 
+void IntrinsicEmitter::EmitIntrinsicToTargetFeaturesTable(
+    const CodeGenIntrinsicTable &Ints, raw_ostream &OS) {
+  StringToOffsetTable Table;
+  for (const CodeGenIntrinsic &Int : Ints)
+    Table.GetOrAddStringOffset(Int.TargetFeatures);
+
+  IfDefEmitter IfDef(OS, "GET_INTRINSIC_TARGET_FEATURES_TABLE");
+  OS << R"(// Intrinsic ID to required target features table.
+// Note that entry #0 is the invalid intrinsic!
+
+)";
+
+  Table.EmitStringTableDef(OS, "IntrinsicTargetFeaturesTable");
+
+  OS << R"(
+static constexpr unsigned IntrinsicTargetFeaturesOffsetTable[] = {
+)";
+
+  OS << "  0, // not_intrinsic\n";
+  for (const CodeGenIntrinsic &Int : Ints) {
+    OS << formatv("  {}, // {}\n", *Table.GetStringOffset(Int.TargetFeatures),
+                  Int.Name);
+  }
+
+  OS << "\n}; // IntrinsicTargetFeaturesOffsetTable\n";
+}
+
 void IntrinsicEmitter::EmitIntrinsicToOverloadTable(
     const CodeGenIntrinsicTable &Ints, raw_ostream &OS) {
   EmitIntrinsicBitTable(
@@ -953,7 +985,7 @@ void IntrinsicEmitter::EmitIntrinsicToBuiltinMap(
 // C front-end. The builtin name is passed in as BuiltinName, and a target
 // prefix (e.g. 'ppc') is passed in as TargetPrefix.
 Intrinsic::ID
-Intrinsic::getIntrinsicFor{}Builtin(StringRef TargetPrefix, 
+Intrinsic::getIntrinsicFor{}Builtin(StringRef TargetPrefix,
                                       StringRef BuiltinName) {{
   using namespace Intrinsic;
 )",


        


More information about the cfe-commits mailing list