https://github.com/shiltian updated https://github.com/llvm/llvm-project/pull/201470
>From bbafbd9b3d5d59801c55c1929d7b82fbee485947 Mon Sep 17 00:00:00 2001 From: Shilei Tian <[email protected]> Date: Wed, 3 Jun 2026 18:48:21 -0400 Subject: [PATCH] [RFC][CodeGen] Add generic target feature checks for intrinsics 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. --- clang/lib/CodeGen/BackendConsumer.h | 3 + clang/lib/CodeGen/CodeGenAction.cpp | 37 +++++++ llvm/docs/ReleaseNotes.md | 5 + .../llvm/CodeGen/TargetSubtargetInfo.h | 23 ++++- llvm/include/llvm/IR/DiagnosticInfo.h | 24 +++++ llvm/include/llvm/IR/Intrinsics.h | 13 +++ llvm/include/llvm/IR/Intrinsics.td | 17 ++++ llvm/include/llvm/IR/IntrinsicsAMDGPU.td | 1 + llvm/include/llvm/MC/MCSubtargetInfo.h | 5 + llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp | 14 +++ .../SelectionDAG/SelectionDAGBuilder.cpp | 25 +++++ llvm/lib/CodeGen/TargetSubtargetInfo.cpp | 18 ++++ llvm/lib/IR/DiagnosticInfo.cpp | 36 +++++++ llvm/lib/IR/Intrinsics.cpp | 9 ++ llvm/lib/MC/MCSubtargetInfo.cpp | 99 ++++++++++++++++++- .../AMDGPU/llvm.amdgcn.permlane16.swap.ll | 7 +- .../TableGen/intrinsic-target-features.td | 28 ++++++ .../TableGen/Basic/CodeGenIntrinsics.cpp | 1 + llvm/utils/TableGen/Basic/CodeGenIntrinsics.h | 1 + .../utils/TableGen/Basic/IntrinsicEmitter.cpp | 34 ++++++- 20 files changed, 391 insertions(+), 9 deletions(-) create mode 100644 llvm/test/TableGen/intrinsic-target-features.td 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..6c9fc8d7af3ce 100644 --- a/clang/lib/CodeGen/CodeGenAction.cpp +++ b/clang/lib/CodeGen/CodeGenAction.cpp @@ -631,6 +631,39 @@ 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 +913,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 df4ced91e8f5e..50f46d667f2fc 100644 --- a/llvm/docs/ReleaseNotes.md +++ b/llvm/docs/ReleaseNotes.md @@ -304,6 +304,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 6f95f0fea6441..62db33214a9c9 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" @@ -29,6 +30,7 @@ namespace llvm { class APInt; +class CallBase; class MachineFunction; class ScheduleDAGMutation; class CallLowering; @@ -87,6 +89,10 @@ class LLVM_ABI TargetSubtargetInfo : public MCSubtargetInfo { virtual bool isXRaySupported() const { return false; } + /// \returns true if the target intrinsic \p IntrinsicID used by \p CB is + /// supported by this subtarget. + bool isIntrinsicSupported(unsigned IntrinsicID, const CallBase &CB) const; + // Interfaces to the major aspects of target machine information: // // -- Instruction opcode and operand information @@ -173,7 +179,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 { @@ -364,6 +370,21 @@ class LLVM_ABI TargetSubtargetInfo : public MCSubtargetInfo { } virtual bool isRegisterReservedByUser(Register R) const { return false; } + +protected: + /// Target hook for intrinsics whose required-feature string is + /// Intrinsic::CustomTargetFeatures. Decides whether \p IntrinsicID as used by + /// \p CB is supported by this subtarget; targets can inspect the call (e.g. + /// the specific overload/mangling or argument combination). Called by + /// isIntrinsicSupported and not cached, so the result may depend on \p CB. + virtual bool isIntrinsicSupportedByTarget(unsigned IntrinsicID, + const CallBase &CB) const { + return true; + } + +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 28fbfa757b1cc..f7e59a68cce18 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 c2411c894d975..85f16e6f6331c 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,18 @@ 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); + +/// Sentinel value for an intrinsic's required-target-features string. When an +/// intrinsic's \c TargetFeatures is set to this keyword, whether the intrinsic +/// is supported is decided by a target-specific hook +/// (TargetSubtargetInfo::isIntrinsicSupportedByTarget) rather than by +/// evaluating a subtarget feature expression. Use it when support depends on +/// more than the subtarget features, such as a particular overload/mangling or +/// argument combination accepted by some targets but not others. +inline constexpr StringLiteral CustomTargetFeatures = "$custom"; + /// 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 d2517fbc4c8b2..9b839923c434b 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -758,6 +758,14 @@ 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, and parentheses group expressions. The special value +// "$custom" (Intrinsic::CustomTargetFeatures) indicates that support is +// decided by a target hook (TargetSubtargetInfo::isIntrinsicSupportedByTarget) +// rather than by evaluating a feature expression; use it when support depends +// on more than the subtarget features, e.g. a particular overload/mangling. // class Intrinsic<list<LLVMType> ret_types, list<LLVMType> param_types = [], @@ -767,6 +775,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; @@ -800,6 +809,14 @@ 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, 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 f1659f0cd803a..a484efb0c1e36 100644 --- a/llvm/include/llvm/IR/IntrinsicsAMDGPU.td +++ b/llvm/include/llvm/IR/IntrinsicsAMDGPU.td @@ -3724,6 +3724,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 708de6d98f40b..e999ac27c6f46 100644 --- a/llvm/include/llvm/MC/MCSubtargetInfo.h +++ b/llvm/include/llvm/MC/MCSubtargetInfo.h @@ -159,6 +159,11 @@ 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, 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..fc8ae73ee1de1 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, CI)) { + 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, CB)) { + 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 c12998df1c445..018f133f17b45 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5517,6 +5517,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, + I)) { + 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..d816050c5d4e1 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,23 @@ TargetSubtargetInfo::TargetSubtargetInfo( TargetSubtargetInfo::~TargetSubtargetInfo() = default; +bool TargetSubtargetInfo::isIntrinsicSupported(unsigned IntrinsicID, + const CallBase &CB) const { + StringRef RequiredFeatures = Intrinsic::getRequiredTargetFeatures( + static_cast<Intrinsic::ID>(IntrinsicID)); + + if (RequiredFeatures.empty()) + return true; + + if (RequiredFeatures == Intrinsic::CustomTargetFeatures) + return isIntrinsicSupportedByTarget(IntrinsicID, CB); + + 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..841b64fdbe5fc 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,41 @@ 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 { + if (RequiredFeatures == Intrinsic::CustomTargetFeatures) + return (Twine(Intrinsic::getBaseName( + static_cast<Intrinsic::ID>(IntrinsicID))) + + " is not supported on this target") + .str(); + 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(Str) << getLocationStr() << ": in function " + << getFunction().getName() << ' ' + << *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 decd700019e6d..f7570f29b4428 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 ed263a2b4817f..e0fd934f66dae 100644 --- a/llvm/lib/MC/MCSubtargetInfo.cpp +++ b/llvm/lib/MC/MCSubtargetInfo.cpp @@ -331,15 +331,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 9ec3314847fe1..71d31584aea9d 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 -new-reg-bank-select -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 -new-reg-bank-select -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=gfx900 -filetype=null < %s 2>&1 | FileCheck -check-prefix=ERR %s +; RUN: not llc -global-isel=1 -global-isel-abort=0 -new-reg-bank-select -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 -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; )", _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
