From d155488891e713be629b65c4a438499f494ebafc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Wed, 6 Aug 2025 18:16:58 +0200 Subject: [PATCH 1/3] [HLSL] Rewrite semantics parsing This is the first PR to implement the semantics proposal: https://github.com/llvm/wg-hlsl/blob/main/proposals/0031-semantics.md This PR focuses on the changes required to handle user semantics, but tried to be almost NFC. What changes is the error messages as the semantics case is not kept when reporting error messages. You might notice the SV_GroupIndex semantic is not properly validated as are others. This is an existing behavior that we'll need to fix, but wanted to keep this separated from this rewrite to stay as-close as an NFC as possible. The next PR will add support on the different kinds of I/O we can have using semantics (input, inout param, structs). --- clang/include/clang/AST/Attr.h | 35 ++++++ clang/include/clang/Basic/Attr.td | 68 +++++----- .../clang/Basic/DiagnosticFrontendKinds.td | 8 ++ .../clang/Basic/DiagnosticParseKinds.td | 5 +- .../clang/Basic/DiagnosticSemaKinds.td | 5 + clang/include/clang/Parse/Parser.h | 8 ++ clang/include/clang/Sema/SemaHLSL.h | 25 +++- clang/lib/Basic/Attributes.cpp | 7 +- clang/lib/CodeGen/CGHLSLRuntime.cpp | 80 +++++++++--- clang/lib/CodeGen/CGHLSLRuntime.h | 28 ++++- clang/lib/Parse/ParseHLSL.cpp | 69 +++++++++-- clang/lib/Sema/SemaDeclAttr.cpp | 18 +-- clang/lib/Sema/SemaHLSL.cpp | 116 ++++++++++++------ .../semantics/DispatchThreadID-noindex.hlsl | 8 ++ .../semantics/SV_GroupID-noindex.hlsl | 9 ++ .../semantics/SV_GroupThreadID-noindex.hlsl | 8 ++ .../CodeGenHLSL/semantics/SV_Position.ps.hlsl | 4 +- clang/test/ParserHLSL/semantic_parsing.hlsl | 2 +- clang/test/SemaHLSL/Semantics/groupindex.hlsl | 12 +- .../Semantics/invalid_entry_parameter.hlsl | 32 ++--- .../SemaHLSL/Semantics/position.ps.size.hlsl | 4 +- .../test/SemaHLSL/Semantics/position.vs.hlsl | 2 +- clang/utils/TableGen/ClangAttrEmitter.cpp | 9 +- 23 files changed, 411 insertions(+), 151 deletions(-) create mode 100644 clang/test/CodeGenHLSL/semantics/DispatchThreadID-noindex.hlsl create mode 100644 clang/test/CodeGenHLSL/semantics/SV_GroupID-noindex.hlsl create mode 100644 clang/test/CodeGenHLSL/semantics/SV_GroupThreadID-noindex.hlsl diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index 994f236337b99..c313b5406f387 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -232,6 +232,41 @@ class HLSLAnnotationAttr : public InheritableAttr { } }; +class HLSLSemanticAttr : public HLSLAnnotationAttr { + unsigned SemanticIndex : 30; + LLVM_PREFERRED_TYPE(bool) + unsigned SemanticIndexable : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned SemanticExplicitIndex : 1; + +protected: + HLSLSemanticAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, + attr::Kind AK, bool IsLateParsed, + bool InheritEvenIfAlreadyPresent, bool SemanticIndexable) + : HLSLAnnotationAttr(Context, CommonInfo, AK, IsLateParsed, + InheritEvenIfAlreadyPresent) { + this->SemanticIndexable = SemanticIndexable; + this->SemanticIndex = 0; + this->SemanticExplicitIndex = 0; + } + +public: + bool isSemanticIndexable() const { return SemanticIndexable; } + + void setSemanticIndex(unsigned SemanticIndex) { + this->SemanticIndex = SemanticIndex; + this->SemanticExplicitIndex = true; + } + + unsigned getSemanticIndex() const { return SemanticIndex; } + + // Implement isa/cast/dyncast/etc. + static bool classof(const Attr *A) { + return A->getKind() >= attr::FirstHLSLSemanticAttr && + A->getKind() <= attr::LastHLSLSemanticAttr; + } +}; + /// A parameter attribute which changes the argument-passing ABI rule /// for the parameter. class ParameterABIAttr : public InheritableParamAttr { diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index b3ff45b3e90a3..bbedc020767a0 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -396,7 +396,7 @@ class ClangGCC } // HLSL Annotation spellings -class HLSLAnnotation : Spelling; +class HLSLAnnotation : Spelling {} class Accessor spellings> { string Name = name; @@ -779,6 +779,16 @@ class DeclOrStmtAttr : InheritableAttr; /// An attribute class for HLSL Annotations. class HLSLAnnotationAttr : InheritableAttr; +class HLSLSemanticAttr : HLSLAnnotationAttr { + bit SemanticIndexable = Indexable; + int SemanticIndex = 0; + bit SemanticExplicitIndex = 0; + + let Spellings = []; + let Subjects = SubjectList<[ParmVar, Field, Function]>; + let LangOpts = [HLSL]; +} + /// A target-specific attribute. This class is meant to be used as a mixin /// with InheritableAttr or Attr depending on the attribute's needs. class TargetSpecificAttr { @@ -4873,27 +4883,6 @@ def HLSLNumThreads: InheritableAttr { let Documentation = [NumThreadsDocs]; } -def HLSLSV_GroupThreadID: HLSLAnnotationAttr { - let Spellings = [HLSLAnnotation<"sv_groupthreadid">]; - let Subjects = SubjectList<[ParmVar, Field]>; - let LangOpts = [HLSL]; - let Documentation = [HLSLSV_GroupThreadIDDocs]; -} - -def HLSLSV_GroupID: HLSLAnnotationAttr { - let Spellings = [HLSLAnnotation<"sv_groupid">]; - let Subjects = SubjectList<[ParmVar, Field]>; - let LangOpts = [HLSL]; - let Documentation = [HLSLSV_GroupIDDocs]; -} - -def HLSLSV_GroupIndex: HLSLAnnotationAttr { - let Spellings = [HLSLAnnotation<"sv_groupindex">]; - let Subjects = SubjectList<[ParmVar, GlobalVar]>; - let LangOpts = [HLSL]; - let Documentation = [HLSLSV_GroupIndexDocs]; -} - def HLSLResourceBinding: InheritableAttr { let Spellings = [HLSLAnnotation<"register">]; let Subjects = SubjectList<[HLSLBufferObj, ExternalGlobalVar], ErrorDiag>; @@ -4943,13 +4932,35 @@ def HLSLResourceBinding: InheritableAttr { }]; } -def HLSLSV_Position : HLSLAnnotationAttr { - let Spellings = [HLSLAnnotation<"sv_position">]; - let Subjects = SubjectList<[ParmVar, Field]>; +def HLSLUnparsedSemantic : HLSLAnnotationAttr { + let Spellings = []; + let Args = [DefaultIntArgument<"Index", 0>, + DefaultBoolArgument<"ExplicitIndex", 0>]; + let Subjects = SubjectList<[ParmVar, Field, Function]>; let LangOpts = [HLSL]; + let Documentation = [InternalOnly]; +} + +def HLSLSV_Position : HLSLSemanticAttr { let Documentation = [HLSLSV_PositionDocs]; } +def HLSLSV_GroupThreadID : HLSLSemanticAttr { + let Documentation = [HLSLSV_GroupThreadIDDocs]; +} + +def HLSLSV_GroupID : HLSLSemanticAttr { + let Documentation = [HLSLSV_GroupIDDocs]; +} + +def HLSLSV_GroupIndex : HLSLSemanticAttr { + let Documentation = [HLSLSV_GroupIndexDocs]; +} + +def HLSLSV_DispatchThreadID : HLSLSemanticAttr { + let Documentation = [HLSLSV_DispatchThreadIDDocs]; +} + def HLSLPackOffset: HLSLAnnotationAttr { let Spellings = [HLSLAnnotation<"packoffset">]; let LangOpts = [HLSL]; @@ -4962,13 +4973,6 @@ def HLSLPackOffset: HLSLAnnotationAttr { }]; } -def HLSLSV_DispatchThreadID: HLSLAnnotationAttr { - let Spellings = [HLSLAnnotation<"sv_dispatchthreadid">]; - let Subjects = SubjectList<[ParmVar, Field]>; - let LangOpts = [HLSL]; - let Documentation = [HLSLSV_DispatchThreadIDDocs]; -} - def HLSLShader : InheritableAttr { let Spellings = [Microsoft<"shader">]; let Subjects = SubjectList<[HLSLEntry]>; diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index 8a8db27490f06..6206e559b4729 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -393,6 +393,14 @@ def warn_hlsl_langstd_minimal : "recommend using %1 instead">, InGroup; +def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">; + +def err_hlsl_semantic_missing : Error<"semantic annotations must be present " + "for all input and outputs of an entry " + "function or patch constant function">; + +def note_hlsl_semantic_used_here : Note<"%0 used here">; + // ClangIR frontend errors def err_cir_to_cir_transform_failed : Error< "CIR-to-CIR transformation failed">, DefaultFatal; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 165f01514e2b1..e8999d4bd7858 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1851,9 +1851,8 @@ def note_max_tokens_total_override : Note<"total token limit set here">; def err_expected_semantic_identifier : Error< "expected HLSL Semantic identifier">; -def err_invalid_declaration_in_hlsl_buffer : Error< - "invalid declaration inside %select{tbuffer|cbuffer}0">; -def err_unknown_hlsl_semantic : Error<"unknown HLSL semantic %0">; +def err_invalid_declaration_in_hlsl_buffer + : Error<"invalid declaration inside %select{tbuffer|cbuffer}0">; def err_hlsl_separate_attr_arg_and_number : Error<"wrong argument format for hlsl attribute, use %0 instead">; def ext_hlsl_access_specifiers : ExtWarn< "access specifiers are a clang HLSL extension">, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 94b174c758a5c..914ec11ba4554 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13058,6 +13058,11 @@ def err_hlsl_duplicate_parameter_modifier : Error<"duplicate parameter modifier def err_hlsl_missing_semantic_annotation : Error< "semantic annotations must be present for all parameters of an entry " "function or patch constant function">; +def err_hlsl_unknown_semantic : Error<"unknown HLSL semantic %0">; +def err_hlsl_semantic_output_not_supported + : Error<"semantic %0 does not support output">; +def err_hlsl_semantic_indexing_not_supported + : Error<"semantic %0 does not allow indexing">; def err_hlsl_init_priority_unsupported : Error< "initializer priorities are not supported in HLSL">; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 683934321a449..f07b4ebe567e5 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -5188,6 +5188,14 @@ class Parser : public CodeCompletionHandler { ParseHLSLAnnotations(Attrs, EndLoc); } + struct ParsedSemantic { + StringRef Name; + unsigned Index; + bool Explicit; + }; + + ParsedSemantic ParseHLSLSemantic(); + void ParseHLSLAnnotations(ParsedAttributes &Attrs, SourceLocation *EndLoc = nullptr, bool CouldBeBitField = false); diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index c0da80a70bb82..a6bcca7a258f6 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -17,6 +17,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" +#include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/SourceLocation.h" #include "clang/Sema/SemaBase.h" #include "llvm/ADT/SmallVector.h" @@ -129,6 +130,7 @@ class SemaHLSL : public SemaBase { bool ActOnUninitializedVarDecl(VarDecl *D); void ActOnEndOfTranslationUnit(TranslationUnitDecl *TU); void CheckEntryPoint(FunctionDecl *FD); + bool isSemanticValid(FunctionDecl *FD, DeclaratorDecl *D); void CheckSemanticAnnotation(FunctionDecl *EntryPoint, const Decl *Param, const HLSLAnnotationAttr *AnnotationAttr); void DiagnoseAttrStageMismatch( @@ -161,16 +163,31 @@ class SemaHLSL : public SemaBase { void handleNumThreadsAttr(Decl *D, const ParsedAttr &AL); void handleWaveSizeAttr(Decl *D, const ParsedAttr &AL); void handleVkConstantIdAttr(Decl *D, const ParsedAttr &AL); - void handleSV_DispatchThreadIDAttr(Decl *D, const ParsedAttr &AL); - void handleSV_GroupThreadIDAttr(Decl *D, const ParsedAttr &AL); - void handleSV_GroupIDAttr(Decl *D, const ParsedAttr &AL); - void handleSV_PositionAttr(Decl *D, const ParsedAttr &AL); void handlePackOffsetAttr(Decl *D, const ParsedAttr &AL); void handleShaderAttr(Decl *D, const ParsedAttr &AL); void handleResourceBindingAttr(Decl *D, const ParsedAttr &AL); void handleParamModifierAttr(Decl *D, const ParsedAttr &AL); bool handleResourceTypeAttr(QualType T, const ParsedAttr &AL); + template + T *createSemanticAttr(const ParsedAttr &AL, + std::optional Location) { + T *Attr = ::new (getASTContext()) T(getASTContext(), AL); + if (Attr->isSemanticIndexable()) + Attr->setSemanticIndex(Location ? *Location : 0); + else if (Location.has_value()) { + Diag(Attr->getLocation(), diag::err_hlsl_semantic_indexing_not_supported) + << Attr->getAttrName()->getName(); + return nullptr; + } + + return Attr; + } + + void diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL, + std::optional Index); + void handleSemanticAttr(Decl *D, const ParsedAttr &AL); + void handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL); bool CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); diff --git a/clang/lib/Basic/Attributes.cpp b/clang/lib/Basic/Attributes.cpp index 81b186f844b8a..5878a4e3f83a4 100644 --- a/clang/lib/Basic/Attributes.cpp +++ b/clang/lib/Basic/Attributes.cpp @@ -189,7 +189,12 @@ AttributeCommonInfo::Kind AttributeCommonInfo::getParsedKind(const IdentifierInfo *Name, const IdentifierInfo *ScopeName, Syntax SyntaxUsed) { - return ::getAttrKind(normalizeName(Name, ScopeName, SyntaxUsed), SyntaxUsed); + AttributeCommonInfo::Kind Kind = + ::getAttrKind(normalizeName(Name, ScopeName, SyntaxUsed), SyntaxUsed); + if (SyntaxUsed == AS_HLSLAnnotation && + Kind == AttributeCommonInfo::Kind::UnknownAttribute) + return AttributeCommonInfo::Kind::AT_HLSLUnparsedSemantic; + return Kind; } AttributeCommonInfo::AttrArgsInfo diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index a47d1cc22980d..346c867e5a0fe 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -22,6 +22,7 @@ #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Type.h" #include "clang/Basic/TargetOptions.h" +#include "clang/Frontend/FrontendDiagnostic.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Frontend/HLSL/RootSignatureMetadata.h" #include "llvm/IR/Constants.h" @@ -383,47 +384,82 @@ static llvm::Value *createSPIRVBuiltinLoad(IRBuilder<> &B, llvm::Module &M, return B.CreateLoad(Ty, GV); } -llvm::Value *CGHLSLRuntime::emitInputSemantic(IRBuilder<> &B, - const ParmVarDecl &D, - llvm::Type *Ty) { - assert(D.hasAttrs() && "Entry parameter missing annotation attribute!"); - if (D.hasAttr()) { +llvm::Value * +CGHLSLRuntime::emitSystemSemanticLoad(IRBuilder<> &B, llvm::Type *Type, + const clang::DeclaratorDecl *Decl, + SemanticInfo &ActiveSemantic) { + if (HLSLSV_GroupIndexAttr *S = + dyn_cast(ActiveSemantic.Semantic)) { llvm::Function *GroupIndex = CGM.getIntrinsic(getFlattenedThreadIdInGroupIntrinsic()); return B.CreateCall(FunctionCallee(GroupIndex)); } - if (D.hasAttr()) { + + if (HLSLSV_DispatchThreadIDAttr *S = + dyn_cast(ActiveSemantic.Semantic)) { llvm::Intrinsic::ID IntrinID = getThreadIdIntrinsic(); llvm::Function *ThreadIDIntrinsic = llvm::Intrinsic::isOverloaded(IntrinID) ? CGM.getIntrinsic(IntrinID, {CGM.Int32Ty}) : CGM.getIntrinsic(IntrinID); - return buildVectorInput(B, ThreadIDIntrinsic, Ty); + return buildVectorInput(B, ThreadIDIntrinsic, Type); } - if (D.hasAttr()) { + + if (HLSLSV_GroupThreadIDAttr *S = + dyn_cast(ActiveSemantic.Semantic)) { llvm::Intrinsic::ID IntrinID = getGroupThreadIdIntrinsic(); llvm::Function *GroupThreadIDIntrinsic = llvm::Intrinsic::isOverloaded(IntrinID) ? CGM.getIntrinsic(IntrinID, {CGM.Int32Ty}) : CGM.getIntrinsic(IntrinID); - return buildVectorInput(B, GroupThreadIDIntrinsic, Ty); + return buildVectorInput(B, GroupThreadIDIntrinsic, Type); } - if (D.hasAttr()) { + + if (HLSLSV_GroupIDAttr *S = + dyn_cast(ActiveSemantic.Semantic)) { llvm::Intrinsic::ID IntrinID = getGroupIdIntrinsic(); llvm::Function *GroupIDIntrinsic = llvm::Intrinsic::isOverloaded(IntrinID) ? CGM.getIntrinsic(IntrinID, {CGM.Int32Ty}) : CGM.getIntrinsic(IntrinID); - return buildVectorInput(B, GroupIDIntrinsic, Ty); + return buildVectorInput(B, GroupIDIntrinsic, Type); } - if (D.hasAttr()) { - if (getArch() == llvm::Triple::spirv) - return createSPIRVBuiltinLoad(B, CGM.getModule(), Ty, "sv_position", - /* BuiltIn::Position */ 0); - llvm_unreachable("SV_Position semantic not implemented for this target."); + + if (HLSLSV_PositionAttr *S = + dyn_cast(ActiveSemantic.Semantic)) { + if (CGM.getTriple().getEnvironment() == Triple::EnvironmentType::Pixel) + return createSPIRVBuiltinLoad(B, CGM.getModule(), Type, + S->getAttrName()->getName(), + /* BuiltIn::FragCoord */ 15); } - assert(false && "Unhandled parameter attribute"); - return nullptr; + + llvm_unreachable("non-handled system semantic. FIXME."); +} + +llvm::Value * +CGHLSLRuntime::handleScalarSemanticLoad(IRBuilder<> &B, llvm::Type *Type, + const clang::DeclaratorDecl *Decl, + SemanticInfo &ActiveSemantic) { + + if (!ActiveSemantic.Semantic) { + ActiveSemantic.Semantic = Decl->getAttr(); + if (!ActiveSemantic.Semantic) { + CGM.getDiags().Report(Decl->getInnerLocStart(), + diag::err_hlsl_semantic_missing); + return nullptr; + } + ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex(); + } + + return emitSystemSemanticLoad(B, Type, Decl, ActiveSemantic); +} + +llvm::Value * +CGHLSLRuntime::handleSemanticLoad(IRBuilder<> &B, llvm::Type *Type, + const clang::DeclaratorDecl *Decl, + SemanticInfo &ActiveSemantic) { + assert(!Type->isStructTy()); + return handleScalarSemanticLoad(B, Type, Decl, ActiveSemantic); } void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD, @@ -440,13 +476,13 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD, Fn->getAttributes().getFnAttrs()); EntryFn->setAttributes(NewAttrs); setHLSLEntryAttributes(FD, EntryFn); + llvm::SmallVector Args; // Set the called function as internal linkage. Fn->setLinkage(GlobalValue::InternalLinkage); BasicBlock *BB = BasicBlock::Create(Ctx, "entry", EntryFn); IRBuilder<> B(BB); - llvm::SmallVector Args; SmallVector OB; if (CGM.shouldEmitConvergenceTokens()) { @@ -459,6 +495,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD, // FIXME: support struct parameters where semantics are on members. // See: https://github.com/llvm/llvm-project/issues/57874 + unsigned SRetOffset = 0; for (const auto &Param : Fn->args()) { if (Param.hasStructRetAttr()) { @@ -468,14 +505,17 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD, Args.emplace_back(PoisonValue::get(Param.getType())); continue; } + const ParmVarDecl *PD = FD->getParamDecl(Param.getArgNo() - SRetOffset); - Args.push_back(emitInputSemantic(B, *PD, Param.getType())); + SemanticInfo ActiveSemantic = {nullptr, 0}; + Args.push_back(handleSemanticLoad(B, Param.getType(), PD, ActiveSemantic)); } CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB); CI->setCallingConv(Fn->getCallingConv()); // FIXME: Handle codegen for return type semantics. // See: https://github.com/llvm/llvm-project/issues/57875 + B.CreateRetVoid(); // Add and identify root signature to function, if applicable diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index 89d2aff85d913..1c78e8bbba7b7 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -21,6 +21,8 @@ #include "llvm/IR/IntrinsicsDirectX.h" #include "llvm/IR/IntrinsicsSPIRV.h" +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/HLSLRuntime.h" @@ -29,6 +31,7 @@ #include "llvm/Frontend/HLSL/HLSLResource.h" #include +#include #include // A function generator macro for picking the right intrinsic @@ -133,8 +136,26 @@ class CGHLSLRuntime { protected: CodeGenModule &CGM; - llvm::Value *emitInputSemantic(llvm::IRBuilder<> &B, const ParmVarDecl &D, - llvm::Type *Ty); + void collectInputSemantic(llvm::IRBuilder<> &B, const DeclaratorDecl *D, + llvm::Type *Type, + SmallVectorImpl &Inputs); + + struct SemanticInfo { + clang::HLSLSemanticAttr *Semantic; + uint32_t Index; + }; + + llvm::Value *emitSystemSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, + const clang::DeclaratorDecl *Decl, + SemanticInfo &ActiveSemantic); + + llvm::Value *handleScalarSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, + const clang::DeclaratorDecl *Decl, + SemanticInfo &ActiveSemantic); + + llvm::Value *handleSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, + const clang::DeclaratorDecl *Decl, + SemanticInfo &ActiveSemantic); public: CGHLSLRuntime(CodeGenModule &CGM) : CGM(CGM) {} @@ -172,6 +193,9 @@ class CGHLSLRuntime { llvm::Triple::ArchType getArch(); llvm::DenseMap LayoutTypes; + + std::unordered_set ActiveInputSemantics; + std::unordered_set ActiveOutputSemantics; }; } // namespace CodeGen diff --git a/clang/lib/Parse/ParseHLSL.cpp b/clang/lib/Parse/ParseHLSL.cpp index e6caa81b309ca..dd2f00c36a034 100644 --- a/clang/lib/Parse/ParseHLSL.cpp +++ b/clang/lib/Parse/ParseHLSL.cpp @@ -118,6 +118,48 @@ static void fixSeparateAttrArgAndNumber(StringRef ArgStr, SourceLocation ArgLoc, Slot = new (Ctx) IdentifierLoc(ArgLoc, PP.getIdentifierInfo(FixedArg)); } +Parser::ParsedSemantic Parser::ParseHLSLSemantic() { + assert(Tok.is(tok::identifier) && "Not a HLSL Annotation"); + + // Semantic pattern: [A-Za-z_]+[0-9]* + // The first part is the semantic name, the second is the optional + // semantic index. + bool Invalid = false; + SmallString<256> Buffer; + Buffer.resize(Tok.getLength() + 1); + StringRef Identifier = PP.getSpelling(Tok, Buffer); + if (Invalid) { + // FIXME: fix error message. + Diag(Tok.getLocation(), diag::err_expected_semantic_identifier); + return {/* Name= */ "", /* Location= */ 0, /* Explicit= */ false}; + } + + unsigned I = 0; + for (; I < Identifier.size() && !isDigit(Identifier[I]); ++I) + continue; + StringRef SemanticName = Identifier.take_front(I); + + if (SemanticName.size() == 0) { + // FIXME: fix error message. + Diag(Tok.getLocation(), diag::err_expected_semantic_identifier); + return {/* Name= */ "", /* Location= */ 0, /* Explicit= */ false}; + } + + unsigned Index = 0; + bool Explicit = I < Identifier.size(); + for (; I < Identifier.size() && isDigit(Identifier[I]); ++I) + Index = Index * 10 + Identifier[I] - '0'; + + // The attribute has letters after the index. + if (I != Identifier.size()) { + // FIXME: fix error message. + Diag(Tok.getLocation(), diag::err_expected_semantic_identifier); + return {/* Name= */ "", /* Location= */ 0, /* Explicit= */ false}; + } + + return {SemanticName, Index, Explicit}; +} + void Parser::ParseHLSLAnnotations(ParsedAttributes &Attrs, SourceLocation *EndLoc, bool CouldBeBitField) { @@ -141,11 +183,15 @@ void Parser::ParseHLSLAnnotations(ParsedAttributes &Attrs, return; } + ParsedAttr::Kind AttrKind = + ParsedAttr::getParsedKind(II, nullptr, ParsedAttr::AS_HLSLAnnotation); + Parser::ParsedSemantic Semantic; + if (AttrKind == ParsedAttr::AT_HLSLUnparsedSemantic) + Semantic = ParseHLSLSemantic(); + SourceLocation Loc = ConsumeToken(); if (EndLoc) *EndLoc = Tok.getLocation(); - ParsedAttr::Kind AttrKind = - ParsedAttr::getParsedKind(II, nullptr, ParsedAttr::AS_HLSLAnnotation); ArgsVector ArgExprs; switch (AttrKind) { @@ -282,14 +328,17 @@ void Parser::ParseHLSLAnnotations(ParsedAttributes &Attrs, return; } } break; - case ParsedAttr::UnknownAttribute: - Diag(Loc, diag::err_unknown_hlsl_semantic) << II; - return; - case ParsedAttr::AT_HLSLSV_GroupThreadID: - case ParsedAttr::AT_HLSLSV_GroupID: - case ParsedAttr::AT_HLSLSV_GroupIndex: - case ParsedAttr::AT_HLSLSV_DispatchThreadID: - case ParsedAttr::AT_HLSLSV_Position: + case ParsedAttr::AT_HLSLUnparsedSemantic: { + ASTContext &Ctx = Actions.getASTContext(); + ArgExprs.push_back(IntegerLiteral::Create( + Ctx, llvm::APInt(Ctx.getTypeSize(Ctx.IntTy), Semantic.Index), Ctx.IntTy, + SourceLocation())); + ArgExprs.push_back(IntegerLiteral::Create( + Ctx, llvm::APInt(1, Semantic.Explicit), Ctx.BoolTy, SourceLocation())); + II = PP.getIdentifierInfo(Semantic.Name.upper()); + break; + } + case ParsedAttr::UnknownAttribute: // FIXME: maybe this is obsolete? break; default: llvm_unreachable("invalid HLSL Annotation"); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 16b18bcb6a2a0..276bcb1365034 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7432,30 +7432,15 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_HLSLWaveSize: S.HLSL().handleWaveSizeAttr(D, AL); break; - case ParsedAttr::AT_HLSLSV_Position: - S.HLSL().handleSV_PositionAttr(D, AL); - break; case ParsedAttr::AT_HLSLVkExtBuiltinInput: S.HLSL().handleVkExtBuiltinInputAttr(D, AL); break; case ParsedAttr::AT_HLSLVkConstantId: S.HLSL().handleVkConstantIdAttr(D, AL); break; - case ParsedAttr::AT_HLSLSV_GroupThreadID: - S.HLSL().handleSV_GroupThreadIDAttr(D, AL); - break; - case ParsedAttr::AT_HLSLSV_GroupID: - S.HLSL().handleSV_GroupIDAttr(D, AL); - break; - case ParsedAttr::AT_HLSLSV_GroupIndex: - handleSimpleAttribute(S, D, AL); - break; case ParsedAttr::AT_HLSLGroupSharedAddressSpace: handleSimpleAttribute(S, D, AL); break; - case ParsedAttr::AT_HLSLSV_DispatchThreadID: - S.HLSL().handleSV_DispatchThreadIDAttr(D, AL); - break; case ParsedAttr::AT_HLSLPackOffset: S.HLSL().handlePackOffsetAttr(D, AL); break; @@ -7468,6 +7453,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_HLSLParamModifier: S.HLSL().handleParamModifierAttr(D, AL); break; + case ParsedAttr::AT_HLSLUnparsedSemantic: + S.HLSL().handleSemanticAttr(D, AL); + break; case ParsedAttr::AT_AbiTag: handleAbiTagAttr(S, D, AL); diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 9276554bebf9d..1617b07fab208 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -747,6 +747,26 @@ void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) { } } +bool SemaHLSL::isSemanticValid(FunctionDecl *FD, DeclaratorDecl *D) { + const auto *AnnotationAttr = D->getAttr(); + if (AnnotationAttr) { + CheckSemanticAnnotation(FD, D, AnnotationAttr); + return true; + } + + const Type *T = D->getType()->getUnqualifiedDesugaredType(); + const RecordType *RT = dyn_cast(T); + if (!RT) + return false; + + const RecordDecl *RD = RT->getDecl(); + for (FieldDecl *Field : RD->fields()) { + if (!isSemanticValid(FD, Field)) + return false; + } + return true; +} + void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) { const auto *ShaderAttr = FD->getAttr(); assert(ShaderAttr && "Entry point has no shader attribute"); @@ -808,11 +828,7 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) { } for (ParmVarDecl *Param : FD->parameters()) { - if (const auto *AnnotationAttr = Param->getAttr()) { - CheckSemanticAnnotation(FD, Param, AnnotationAttr); - } else { - // FIXME: Handle struct parameters where annotations are on struct fields. - // See: https://github.com/llvm/llvm-project/issues/57875 + if (!isSemanticValid(FD, Param)) { Diag(FD->getLocation(), diag::err_hlsl_missing_semantic_annotation); Diag(Param->getLocation(), diag::note_previous_decl) << Param; FD->setInvalidDecl(); @@ -838,8 +854,6 @@ void SemaHLSL::CheckSemanticAnnotation( DiagnoseAttrStageMismatch(AnnotationAttr, ST, {llvm::Triple::Compute}); break; case attr::HLSLSV_Position: - // TODO(#143523): allow use on other shader types & output once the overall - // semantic logic is implemented. if (ST == llvm::Triple::Pixel) return; DiagnoseAttrStageMismatch(AnnotationAttr, ST, {llvm::Triple::Pixel}); @@ -1492,18 +1506,8 @@ bool SemaHLSL::diagnoseInputIDType(QualType T, const ParsedAttr &AL) { return true; } -void SemaHLSL::handleSV_DispatchThreadIDAttr(Decl *D, const ParsedAttr &AL) { - auto *VD = cast(D); - if (!diagnoseInputIDType(VD->getType(), AL)) - return; - - D->addAttr(::new (getASTContext()) - HLSLSV_DispatchThreadIDAttr(getASTContext(), AL)); -} - bool SemaHLSL::diagnosePositionType(QualType T, const ParsedAttr &AL) { const auto *VT = T->getAs(); - if (!T->hasFloatingRepresentation() || (VT && VT->getNumElements() > 4)) { Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type) << AL << "float/float1/float2/float3/float4"; @@ -1513,29 +1517,71 @@ bool SemaHLSL::diagnosePositionType(QualType T, const ParsedAttr &AL) { return true; } -void SemaHLSL::handleSV_PositionAttr(Decl *D, const ParsedAttr &AL) { - auto *VD = cast(D); - if (!diagnosePositionType(VD->getType(), AL)) - return; - - D->addAttr(::new (getASTContext()) HLSLSV_PositionAttr(getASTContext(), AL)); -} +void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL, + std::optional Index) { + StringRef SemanticName = AL.getAttrName()->getName(); -void SemaHLSL::handleSV_GroupThreadIDAttr(Decl *D, const ParsedAttr &AL) { auto *VD = cast(D); - if (!diagnoseInputIDType(VD->getType(), AL)) - return; - - D->addAttr(::new (getASTContext()) - HLSLSV_GroupThreadIDAttr(getASTContext(), AL)); -} + QualType ValueType = VD->getType(); + if (auto *FD = dyn_cast(D)) + ValueType = FD->getReturnType(); + + bool IsOutput = false; + if (HLSLParamModifierAttr *MA = D->getAttr()) { + if (MA->isOut()) { + IsOutput = true; + ValueType = cast(ValueType)->getPointeeType(); + } + } -void SemaHLSL::handleSV_GroupIDAttr(Decl *D, const ParsedAttr &AL) { - auto *VD = cast(D); - if (!diagnoseInputIDType(VD->getType(), AL)) +#define CHECK_OUTPUT_FORBIDDEN(AL) \ + if (IsOutput) { \ + Diag(AL.getLoc(), diag::err_hlsl_semantic_output_not_supported) << AL; \ + } + + Attr *Attribute = nullptr; + if (SemanticName == "SV_DISPATCHTHREADID") { + diagnoseInputIDType(ValueType, AL); + CHECK_OUTPUT_FORBIDDEN(AL); + Attribute = createSemanticAttr(AL, Index); + } else if (SemanticName == "SV_GROUPINDEX") { + CHECK_OUTPUT_FORBIDDEN(AL); + Attribute = createSemanticAttr(AL, Index); + } else if (SemanticName == "SV_GROUPTHREADID") { + diagnoseInputIDType(ValueType, AL); + CHECK_OUTPUT_FORBIDDEN(AL); + Attribute = createSemanticAttr(AL, Index); + } else if (SemanticName == "SV_GROUPID") { + diagnoseInputIDType(ValueType, AL); + CHECK_OUTPUT_FORBIDDEN(AL); + Attribute = createSemanticAttr(AL, Index); + } else if (SemanticName == "SV_POSITION") { + const auto *VT = ValueType->getAs(); + if (!ValueType->hasFloatingRepresentation() || + (VT && VT->getNumElements() > 4)) + Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type) + << AL << "float/float1/float2/float3/float4"; + Attribute = createSemanticAttr(AL, Index); + } else + Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL; + + if (!Attribute) return; + D->addAttr(Attribute); +} - D->addAttr(::new (getASTContext()) HLSLSV_GroupIDAttr(getASTContext(), AL)); +void SemaHLSL::handleSemanticAttr(Decl *D, const ParsedAttr &AL) { + uint32_t IndexValue, ExplicitIndex; + SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), IndexValue); + SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(1), ExplicitIndex); + assert(IndexValue > 0 ? ExplicitIndex : true); + std::optional Index = + ExplicitIndex ? std::optional(IndexValue) : std::nullopt; + + if (AL.getAttrName()->getName().starts_with("SV_")) + diagnoseSystemSemanticAttr(D, AL, Index); + else + Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL; } void SemaHLSL::handlePackOffsetAttr(Decl *D, const ParsedAttr &AL) { diff --git a/clang/test/CodeGenHLSL/semantics/DispatchThreadID-noindex.hlsl b/clang/test/CodeGenHLSL/semantics/DispatchThreadID-noindex.hlsl new file mode 100644 index 0000000000000..c75efff82a852 --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/DispatchThreadID-noindex.hlsl @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note,error +// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note,error + +[shader("compute")] +[numthreads(8,8,1)] +void foo(uint Idx : SV_DispatchThreadID1) { + // expected-error@-1 {{semantic SV_DISPATCHTHREADID does not allow indexing}} +} diff --git a/clang/test/CodeGenHLSL/semantics/SV_GroupID-noindex.hlsl b/clang/test/CodeGenHLSL/semantics/SV_GroupID-noindex.hlsl new file mode 100644 index 0000000000000..d18ab9f5d0ff7 --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/SV_GroupID-noindex.hlsl @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note,error +// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note,error + +[shader("compute")] +[numthreads(8,8,1)] +void foo(uint Idx : SV_GroupID1) { + // expected-error@-1 {{semantic SV_GROUPID does not allow indexing}} +} + diff --git a/clang/test/CodeGenHLSL/semantics/SV_GroupThreadID-noindex.hlsl b/clang/test/CodeGenHLSL/semantics/SV_GroupThreadID-noindex.hlsl new file mode 100644 index 0000000000000..55b1f349dd943 --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/SV_GroupThreadID-noindex.hlsl @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note,error +// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note,error + +[shader("compute")] +[numthreads(8,8,1)] +void foo(uint Idx : SV_GroupThreadID1) { + // expected-error@-1 {{semantic SV_GROUPTHREADID does not allow indexing}} +} diff --git a/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl b/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl index bdba38e028edd..af13eb3ea6af5 100644 --- a/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl +++ b/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl @@ -1,10 +1,10 @@ // RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s -// CHECK: @sv_position = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations !0 +// CHECK: @SV_POSITION = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations !0 // CHECK: define void @main() {{.*}} { float4 main(float4 p : SV_Position) { - // CHECK: %[[#P:]] = load <4 x float>, ptr addrspace(7) @sv_position, align 16 + // CHECK: %[[#P:]] = load <4 x float>, ptr addrspace(7) @SV_POSITION, align 16 // CHECK: %[[#R:]] = call spir_func <4 x float> @_Z4mainDv4_f(<4 x float> %[[#P]]) return p; } diff --git a/clang/test/ParserHLSL/semantic_parsing.hlsl b/clang/test/ParserHLSL/semantic_parsing.hlsl index 34df1805c5a95..1301c84e0211b 100644 --- a/clang/test/ParserHLSL/semantic_parsing.hlsl +++ b/clang/test/ParserHLSL/semantic_parsing.hlsl @@ -3,5 +3,5 @@ // expected-error@+1 {{expected HLSL Semantic identifier}} void Entry(int GI : ) { } -// expected-error@+1 {{unknown HLSL semantic 'SV_IWantAPony'}} +// expected-error@+1 {{unknown HLSL semantic 'SV_IWANTAPONY'}} void Pony(int GI : SV_IWantAPony) { } diff --git a/clang/test/SemaHLSL/Semantics/groupindex.hlsl b/clang/test/SemaHLSL/Semantics/groupindex.hlsl index a33e060c82906..8acdee8e85989 100644 --- a/clang/test/SemaHLSL/Semantics/groupindex.hlsl +++ b/clang/test/SemaHLSL/Semantics/groupindex.hlsl @@ -4,26 +4,26 @@ [shader("compute")][numthreads(32,1,1)] void compute(int GI : SV_GroupIndex) {} -// expected-error@+2 {{attribute 'SV_GroupIndex' is unsupported in 'pixel' shaders}} +// expected-error@+2 {{attribute 'SV_GROUPINDEX' is unsupported in 'pixel' shaders}} [shader("pixel")] void pixel(int GI : SV_GroupIndex) {} -// expected-error@+2 {{attribute 'SV_GroupIndex' is unsupported in 'vertex' shaders}} +// expected-error@+2 {{attribute 'SV_GROUPINDEX' is unsupported in 'vertex' shaders}} [shader("vertex")] void vertex(int GI : SV_GroupIndex) {} -// expected-error@+2 {{attribute 'SV_GroupIndex' is unsupported in 'geometry' shaders}} +// expected-error@+2 {{attribute 'SV_GROUPINDEX' is unsupported in 'geometry' shaders}} [shader("geometry")] void geometry(int GI : SV_GroupIndex) {} -// expected-error@+2 {{attribute 'SV_GroupIndex' is unsupported in 'domain' shaders}} +// expected-error@+2 {{attribute 'SV_GROUPINDEX' is unsupported in 'domain' shaders}} [shader("domain")] void domain(int GI : SV_GroupIndex) {} -// expected-error@+2 {{attribute 'SV_GroupIndex' is unsupported in 'amplification' shaders}} +// expected-error@+2 {{attribute 'SV_GROUPINDEX' is unsupported in 'amplification' shaders}} [shader("amplification")][numthreads(32,1,1)] void amplification(int GI : SV_GroupIndex) {} -// expected-error@+2 {{attribute 'SV_GroupIndex' is unsupported in 'mesh' shaders}} +// expected-error@+2 {{attribute 'SV_GROUPINDEX' is unsupported in 'mesh' shaders}} [shader("mesh")][numthreads(32,1,1)] void mesh(int GI : SV_GroupIndex) {} diff --git a/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl b/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl index 1bb4ee5182d62..18bae5ee322df 100644 --- a/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl +++ b/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -x hlsl -ast-dump -verify -o - %s [numthreads(8,8,1)] -// expected-error@+1 {{attribute 'SV_DispatchThreadID' only applies to a field or parameter of type 'uint/uint2/uint3'}} +// expected-error@+1 {{attribute 'SV_DISPATCHTHREADID' only applies to a field or parameter of type 'uint/uint2/uint3'}} void CSMain(float ID : SV_DispatchThreadID) { } @@ -11,71 +11,71 @@ struct ST { float b; }; [numthreads(8,8,1)] -// expected-error@+1 {{attribute 'SV_DispatchThreadID' only applies to a field or parameter of type 'uint/uint2/uint3'}} +// expected-error@+1 {{attribute 'SV_DISPATCHTHREADID' only applies to a field or parameter of type 'uint/uint2/uint3'}} void CSMain2(ST ID : SV_DispatchThreadID) { } void foo() { -// expected-warning@+1 {{'SV_DispatchThreadID' attribute only applies to parameters and non-static data members}} +// expected-warning@+1 {{'SV_DISPATCHTHREADID' attribute only applies to parameters, non-static data members, and functions}} uint V : SV_DispatchThreadID; } struct ST2 { -// expected-warning@+1 {{'SV_DispatchThreadID' attribute only applies to parameters and non-static data members}} +// expected-warning@+1 {{'SV_DISPATCHTHREADID' attribute only applies to parameters, non-static data members, and functions}} static uint X : SV_DispatchThreadID; uint s : SV_DispatchThreadID; }; [numthreads(8,8,1)] -// expected-error@+1 {{attribute 'SV_GroupID' only applies to a field or parameter of type 'uint/uint2/uint3'}} +// expected-error@+1 {{attribute 'SV_GROUPID' only applies to a field or parameter of type 'uint/uint2/uint3'}} void CSMain_GID(float ID : SV_GroupID) { } [numthreads(8,8,1)] -// expected-error@+1 {{attribute 'SV_GroupID' only applies to a field or parameter of type 'uint/uint2/uint3'}} +// expected-error@+1 {{attribute 'SV_GROUPID' only applies to a field or parameter of type 'uint/uint2/uint3'}} void CSMain2_GID(ST GID : SV_GroupID) { } void foo_GID() { -// expected-warning@+1 {{'SV_GroupID' attribute only applies to parameters and non-static data members}} +// expected-warning@+1 {{'SV_GROUPID' attribute only applies to parameters, non-static data members, and functions}} uint GIS : SV_GroupID; } struct ST2_GID { -// expected-warning@+1 {{'SV_GroupID' attribute only applies to parameters and non-static data members}} +// expected-warning@+1 {{'SV_GROUPID' attribute only applies to parameters, non-static data members, and functions}} static uint GID : SV_GroupID; uint s_gid : SV_GroupID; }; [numthreads(8,8,1)] -// expected-error@+1 {{attribute 'SV_GroupThreadID' only applies to a field or parameter of type 'uint/uint2/uint3'}} +// expected-error@+1 {{attribute 'SV_GROUPTHREADID' only applies to a field or parameter of type 'uint/uint2/uint3'}} void CSMain_GThreadID(float ID : SV_GroupThreadID) { } [numthreads(8,8,1)] -// expected-error@+1 {{attribute 'SV_GroupThreadID' only applies to a field or parameter of type 'uint/uint2/uint3'}} +// expected-error@+1 {{attribute 'SV_GROUPTHREADID' only applies to a field or parameter of type 'uint/uint2/uint3'}} void CSMain2_GThreadID(ST GID : SV_GroupThreadID) { } void foo_GThreadID() { -// expected-warning@+1 {{'SV_GroupThreadID' attribute only applies to parameters and non-static data members}} +// expected-warning@+1 {{'SV_GROUPTHREADID' attribute only applies to parameters, non-static data members, and functions}} uint GThreadIS : SV_GroupThreadID; } struct ST2_GThreadID { -// expected-warning@+1 {{'SV_GroupThreadID' attribute only applies to parameters and non-static data members}} +// expected-warning@+1 {{'SV_GROUPTHREADID' attribute only applies to parameters, non-static data members, and functions}} static uint GThreadID : SV_GroupThreadID; uint s_gthreadid : SV_GroupThreadID; }; [shader("vertex")] -// expected-error@+4 {{attribute 'SV_GroupIndex' is unsupported in 'vertex' shaders, requires compute}} -// expected-error@+3 {{attribute 'SV_DispatchThreadID' is unsupported in 'vertex' shaders, requires compute}} -// expected-error@+2 {{attribute 'SV_GroupID' is unsupported in 'vertex' shaders, requires compute}} -// expected-error@+1 {{attribute 'SV_GroupThreadID' is unsupported in 'vertex' shaders, requires compute}} +// expected-error@+4 {{attribute 'SV_GROUPINDEX' is unsupported in 'vertex' shaders, requires compute}} +// expected-error@+3 {{attribute 'SV_DISPATCHTHREADID' is unsupported in 'vertex' shaders, requires compute}} +// expected-error@+2 {{attribute 'SV_GROUPID' is unsupported in 'vertex' shaders, requires compute}} +// expected-error@+1 {{attribute 'SV_GROUPTHREADID' is unsupported in 'vertex' shaders, requires compute}} void vs_main(int GI : SV_GroupIndex, uint ID : SV_DispatchThreadID, uint GID : SV_GroupID, uint GThreadID : SV_GroupThreadID) {} diff --git a/clang/test/SemaHLSL/Semantics/position.ps.size.hlsl b/clang/test/SemaHLSL/Semantics/position.ps.size.hlsl index 124d401a9990c..b57ce4e325ca2 100644 --- a/clang/test/SemaHLSL/Semantics/position.ps.size.hlsl +++ b/clang/test/SemaHLSL/Semantics/position.ps.size.hlsl @@ -1,10 +1,10 @@ // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -finclude-default-header -o - %s -verify -verify-ignore-unexpected // RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-library -x hlsl -finclude-default-header -o - %s -verify -verify-ignore-unexpected -// expected-error@+1 {{attribute 'SV_Position' only applies to a field or parameter of type 'float/float1/float2/float3/float4'}} +// expected-error@+1 {{attribute 'SV_POSITION' only applies to a field or parameter of type 'float/float1/float2/float3/float4'}} void main(vector a : SV_Position) { } -// expected-error@+1 {{attribute 'SV_Position' only applies to a field or parameter of type 'float/float1/float2/float3/float4'}} +// expected-error@+1 {{attribute 'SV_POSITION' only applies to a field or parameter of type 'float/float1/float2/float3/float4'}} void main(int2 a : SV_Position) { } diff --git a/clang/test/SemaHLSL/Semantics/position.vs.hlsl b/clang/test/SemaHLSL/Semantics/position.vs.hlsl index 19f781fa3757c..e5e8da2484d93 100644 --- a/clang/test/SemaHLSL/Semantics/position.vs.hlsl +++ b/clang/test/SemaHLSL/Semantics/position.vs.hlsl @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-vertex -x hlsl -finclude-default-header -o - %s -verify -// expected-error@+1 {{attribute 'SV_Position' is unsupported in 'vertex' shaders, requires pixel}} +// expected-error@+1 {{attribute 'SV_POSITION' is unsupported in 'vertex' shaders, requires pixel}} float4 main(float4 a : SV_Position) { return a; } diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index d63e79a5f5155..c4193110bee01 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -2725,12 +2725,15 @@ static void emitAttributes(const RecordKeeper &Records, raw_ostream &OS, assert(!Supers.empty() && "Forgot to specify a superclass for the attr"); std::string SuperName; bool Inheritable = false; + bool HLSLSemantic = false; for (const Record *R : reverse(Supers)) { if (R->getName() != "TargetSpecificAttr" && R->getName() != "DeclOrTypeAttr" && SuperName.empty()) SuperName = R->getName().str(); if (R->getName() == "InheritableAttr") Inheritable = true; + if (R->getName() == "HLSLSemanticAttr") + HLSLSemantic = true; } if (Header) @@ -3054,6 +3057,9 @@ static void emitAttributes(const RecordKeeper &Records, raw_ostream &OS, << (R.getValueAsBit("InheritEvenIfAlreadyPresent") ? "true" : "false"); } + if (HLSLSemantic) { + OS << ", " << (R.getValueAsBit("SemanticIndexable") ? "true" : "false"); + } OS << ")\n"; for (auto const &ai : Args) { @@ -3270,7 +3276,8 @@ static const AttrClassDescriptor AttrClassDescriptors[] = { {"INHERITABLE_PARAM_ATTR", "InheritableParamAttr"}, {"INHERITABLE_PARAM_OR_STMT_ATTR", "InheritableParamOrStmtAttr"}, {"PARAMETER_ABI_ATTR", "ParameterABIAttr"}, - {"HLSL_ANNOTATION_ATTR", "HLSLAnnotationAttr"}}; + {"HLSL_ANNOTATION_ATTR", "HLSLAnnotationAttr"}, + {"HLSL_SEMANTIC_ATTR", "HLSLSemanticAttr"}}; static void emitDefaultDefine(raw_ostream &OS, StringRef name, const char *superName) { From f299e4018645ab0c97da0a0972523e3029d2a947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Tue, 12 Aug 2025 18:06:17 +0200 Subject: [PATCH 2/3] [HLSL] Add support for input semantics in structs This commit adds the support for semantics annotations on structs, but only for inputs. Due to the current semantics implemented, we cannot test much more than nesting/shadowing. Once user semantics are implemented, we'll be able to test arrays in structs and more complex cases. As-is, this commit has one weakness vs DXC: semantics type validation is not looking at the inner-most type, but the outermost type: ```hlsl struct Inner { uint tid; }; Inner inner : SV_GroupID ``` This sample would fail today because `SV_GroupID` require the type to be an integer. This works in DXC as the inner type is a integer. Because GroupIndex is not correctly validated, I uses this semantic to test the inheritance/shadowing. But this will need to be fixed in a later commit. Requires #152537 --- clang/lib/CodeGen/CGHLSLRuntime.cpp | 63 ++++++++++++++++++- clang/lib/CodeGen/CGHLSLRuntime.h | 4 ++ .../semantics/semantic-struct-1.hlsl | 23 +++++++ .../semantics/semantic-struct-2.hlsl | 25 ++++++++ .../semantic-struct-nested-inherit.hlsl | 30 +++++++++ .../semantic-struct-nested-shadow.hlsl | 30 +++++++++ .../semantics/semantic-struct-nested.hlsl | 30 +++++++++ 7 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 clang/test/CodeGenHLSL/semantics/semantic-struct-1.hlsl create mode 100644 clang/test/CodeGenHLSL/semantics/semantic-struct-2.hlsl create mode 100644 clang/test/CodeGenHLSL/semantics/semantic-struct-nested-inherit.hlsl create mode 100644 clang/test/CodeGenHLSL/semantics/semantic-struct-nested-shadow.hlsl create mode 100644 clang/test/CodeGenHLSL/semantics/semantic-struct-nested.hlsl diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 346c867e5a0fe..e7ae242f3b90a 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -454,11 +454,51 @@ CGHLSLRuntime::handleScalarSemanticLoad(IRBuilder<> &B, llvm::Type *Type, return emitSystemSemanticLoad(B, Type, Decl, ActiveSemantic); } +llvm::Value * +CGHLSLRuntime::handleStructSemanticLoad(IRBuilder<> &B, llvm::Type *Type, + const clang::DeclaratorDecl *Decl, + SemanticInfo &ActiveSemantic) { + const llvm::StructType *ST = cast(Type); + const clang::RecordDecl *RD = Decl->getType()->getAsRecordDecl(); + + assert(std::distance(RD->field_begin(), RD->field_end()) == + ST->getNumElements()); + + if (!ActiveSemantic.Semantic) { + ActiveSemantic.Semantic = Decl->getAttr(); + ActiveSemantic.Index = ActiveSemantic.Semantic + ? ActiveSemantic.Semantic->getSemanticIndex() + : 0; + } + + llvm::Value *Aggregate = llvm::PoisonValue::get(Type); + auto FieldDecl = RD->field_begin(); + for (unsigned I = 0; I < ST->getNumElements(); ++I) { + SemanticInfo Info = ActiveSemantic; + llvm::Value *ChildValue = + handleSemanticLoad(B, ST->getElementType(I), *FieldDecl, Info); + if (!ChildValue) { + CGM.getDiags().Report(Decl->getInnerLocStart(), + diag::note_hlsl_semantic_used_here) + << Decl; + return nullptr; + } + if (ActiveSemantic.Semantic) + ActiveSemantic = Info; + + Aggregate = B.CreateInsertValue(Aggregate, ChildValue, I); + ++FieldDecl; + } + + return Aggregate; +} + llvm::Value * CGHLSLRuntime::handleSemanticLoad(IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl, SemanticInfo &ActiveSemantic) { - assert(!Type->isStructTy()); + if (Type->isStructTy()) + return handleStructSemanticLoad(B, Type, Decl, ActiveSemantic); return handleScalarSemanticLoad(B, Type, Decl, ActiveSemantic); } @@ -507,8 +547,25 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD, } const ParmVarDecl *PD = FD->getParamDecl(Param.getArgNo() - SRetOffset); - SemanticInfo ActiveSemantic = {nullptr, 0}; - Args.push_back(handleSemanticLoad(B, Param.getType(), PD, ActiveSemantic)); + llvm::Value *SemanticValue = nullptr; + if (HLSLParamModifierAttr *MA = PD->getAttr()) { + llvm_unreachable("Not handled yet"); + } else { + llvm::Type *ParamType = + Param.hasByValAttr() ? Param.getParamByValType() : Param.getType(); + SemanticInfo ActiveSemantic = {nullptr, 0}; + SemanticValue = handleSemanticLoad(B, ParamType, PD, ActiveSemantic); + if (!SemanticValue) + return; + if (Param.hasByValAttr()) { + llvm::Value *Var = B.CreateAlloca(Param.getParamByValType()); + B.CreateStore(SemanticValue, Var); + SemanticValue = Var; + } + } + + assert(SemanticValue); + Args.push_back(SemanticValue); } CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB); diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index 1c78e8bbba7b7..93f54e41d524e 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -153,6 +153,10 @@ class CGHLSLRuntime { const clang::DeclaratorDecl *Decl, SemanticInfo &ActiveSemantic); + llvm::Value *handleStructSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, + const clang::DeclaratorDecl *Decl, + SemanticInfo &ActiveSemantic); + llvm::Value *handleSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl, SemanticInfo &ActiveSemantic); diff --git a/clang/test/CodeGenHLSL/semantics/semantic-struct-1.hlsl b/clang/test/CodeGenHLSL/semantics/semantic-struct-1.hlsl new file mode 100644 index 0000000000000..ddd0baed41f37 --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/semantic-struct-1.hlsl @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx +// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV -DTARGET=spv + + +struct Input { + uint Idx : SV_DispatchThreadID; + +}; + +// Make sure SV_DispatchThreadID translated into dx.thread.id. + +// CHECK: define void @foo() +// CHECK-DXIL: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0) +// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0) +// CHECK: %[[#TMP:]] = insertvalue %struct.Input poison, i32 %[[#ID]], 0 +// CHECK: %[[#VAR:]] = alloca %struct.Input, align 8 +// CHECK: store %struct.Input %[[#TMP]], ptr %[[#VAR]], align 4 +// CHECK-DXIL: call void @{{.*}}foo{{.*}}(ptr %[[#VAR]]) +// CHECK-SPIRV: call spir_func void @{{.*}}foo{{.*}}(ptr %[[#VAR]]) +[shader("compute")] +[numthreads(8,8,1)] +void foo(Input input) {} + diff --git a/clang/test/CodeGenHLSL/semantics/semantic-struct-2.hlsl b/clang/test/CodeGenHLSL/semantics/semantic-struct-2.hlsl new file mode 100644 index 0000000000000..0d9c91e746454 --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/semantic-struct-2.hlsl @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx +// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV -DTARGET=spv + + +struct Input { + uint Idx : SV_DispatchThreadID; + uint Gid : SV_GroupID; +}; + +// Make sure SV_DispatchThreadID translated into dx.thread.id. + +// CHECK: define void @foo() +// CHECK-DXIL: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0) +// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0) +// CHECK: %[[#TMP1:]] = insertvalue %struct.Input poison, i32 %[[#ID]], 0 +// CHECK-DXIL: %[[#GID:]] = call i32 @llvm.[[TARGET]].group.id(i32 0) +// CHECK-SPIRV:%[[#GID:]] = call i32 @llvm.[[TARGET]].group.id.i32(i32 0) +// CHECK: %[[#TMP2:]] = insertvalue %struct.Input %[[#TMP1]], i32 %[[#GID]], 1 +// CHECK: %[[#VAR:]] = alloca %struct.Input, align 8 +// CHECK: store %struct.Input %[[#TMP2]], ptr %[[#VAR]], align 4 +// CHECK-DXIL: call void @{{.*}}foo{{.*}}(ptr %[[#VAR]]) +// CHECK-SPIRV: call spir_func void @{{.*}}foo{{.*}}(ptr %[[#VAR]]) +[shader("compute")] +[numthreads(8,8,1)] +void foo(Input input) {} diff --git a/clang/test/CodeGenHLSL/semantics/semantic-struct-nested-inherit.hlsl b/clang/test/CodeGenHLSL/semantics/semantic-struct-nested-inherit.hlsl new file mode 100644 index 0000000000000..f4c4d86933ca1 --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/semantic-struct-nested-inherit.hlsl @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx +// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV -DTARGET=spv + + +struct Inner { + uint Gid; +}; + +struct Input { + uint Idx : SV_DispatchThreadID; + Inner inner : SV_GroupIndex; +}; + +// Make sure SV_DispatchThreadID translated into dx.thread.id. + +// CHECK: define void @foo() +// CHECK-DXIL: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0) +// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0) +// CHECK: %[[#TMP1:]] = insertvalue %struct.Input poison, i32 %[[#ID]], 0 +// CHECK-DXIL: %[[#GID:]] = call i32 @llvm.dx.flattened.thread.id.in.group() +// CHECK-SPIRV:%[[#GID:]] = call i32 @llvm.spv.flattened.thread.id.in.group() +// CHECK: %[[#TMP2:]] = insertvalue %struct.Inner poison, i32 %[[#GID]], 0 +// CHECK: %[[#TMP3:]] = insertvalue %struct.Input %[[#TMP1]], %struct.Inner %[[#TMP2]], 1 +// CHECK: %[[#VAR:]] = alloca %struct.Input, align 8 +// CHECK: store %struct.Input %[[#TMP3]], ptr %[[#VAR]], align 4 +// CHECK-DXIL: call void @{{.*}}foo{{.*}}(ptr %[[#VAR]]) +// CHECK-SPIRV: call spir_func void @{{.*}}foo{{.*}}(ptr %[[#VAR]]) +[shader("compute")] +[numthreads(8,8,1)] +void foo(Input input) {} diff --git a/clang/test/CodeGenHLSL/semantics/semantic-struct-nested-shadow.hlsl b/clang/test/CodeGenHLSL/semantics/semantic-struct-nested-shadow.hlsl new file mode 100644 index 0000000000000..e1344dd87a6ed --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/semantic-struct-nested-shadow.hlsl @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx +// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV -DTARGET=spv + + +struct Inner { + uint Gid : SV_GroupID; +}; + +struct Input { + uint Idx : SV_DispatchThreadID; + Inner inner : SV_GroupIndex; +}; + +// Make sure SV_DispatchThreadID translated into dx.thread.id. + +// CHECK: define void @foo() +// CHECK-DXIL: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0) +// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0) +// CHECK: %[[#TMP1:]] = insertvalue %struct.Input poison, i32 %[[#ID]], 0 +// CHECK-DXIL: %[[#GID:]] = call i32 @llvm.dx.flattened.thread.id.in.group() +// CHECK-SPIRV:%[[#GID:]] = call i32 @llvm.spv.flattened.thread.id.in.group() +// CHECK: %[[#TMP2:]] = insertvalue %struct.Inner poison, i32 %[[#GID]], 0 +// CHECK: %[[#TMP3:]] = insertvalue %struct.Input %[[#TMP1]], %struct.Inner %[[#TMP2]], 1 +// CHECK: %[[#VAR:]] = alloca %struct.Input, align 8 +// CHECK: store %struct.Input %[[#TMP3]], ptr %[[#VAR]], align 4 +// CHECK-DXIL: call void @{{.*}}foo{{.*}}(ptr %[[#VAR]]) +// CHECK-SPIRV: call spir_func void @{{.*}}foo{{.*}}(ptr %[[#VAR]]) +[shader("compute")] +[numthreads(8,8,1)] +void foo(Input input) {} diff --git a/clang/test/CodeGenHLSL/semantics/semantic-struct-nested.hlsl b/clang/test/CodeGenHLSL/semantics/semantic-struct-nested.hlsl new file mode 100644 index 0000000000000..cd6f9460bc617 --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/semantic-struct-nested.hlsl @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx +// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV -DTARGET=spv + + +struct Inner { + uint Gid : SV_GroupID; +}; + +struct Input { + uint Idx : SV_DispatchThreadID; + Inner inner; +}; + +// Make sure SV_DispatchThreadID translated into dx.thread.id. + +// CHECK: define void @foo() +// CHECK-DXIL: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0) +// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0) +// CHECK: %[[#TMP1:]] = insertvalue %struct.Input poison, i32 %[[#ID]], 0 +// CHECK-DXIL: %[[#GID:]] = call i32 @llvm.[[TARGET]].group.id(i32 0) +// CHECK-SPIRV:%[[#GID:]] = call i32 @llvm.[[TARGET]].group.id.i32(i32 0) +// CHECK: %[[#TMP2:]] = insertvalue %struct.Inner poison, i32 %[[#GID]], 0 +// CHECK: %[[#TMP3:]] = insertvalue %struct.Input %[[#TMP1]], %struct.Inner %[[#TMP2]], 1 +// CHECK: %[[#VAR:]] = alloca %struct.Input, align 8 +// CHECK: store %struct.Input %[[#TMP3]], ptr %[[#VAR]], align 4 +// CHECK-DXIL: call void @{{.*}}foo{{.*}}(ptr %[[#VAR]]) +// CHECK-SPIRV: call spir_func void @{{.*}}foo{{.*}}(ptr %[[#VAR]]) +[shader("compute")] +[numthreads(8,8,1)] +void foo(Input input) {} From b03a19f696da5c9fe2ea960a7e5e3dce6e3b2ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Wed, 13 Aug 2025 15:58:43 +0200 Subject: [PATCH 3/3] [HLSL] Add support for user semantics This commit depends on #152537 and #153224 This commit adds support for HLSL input semantics. User semantics are all semantics not starting with `SV_`. --- clang/include/clang/Basic/Attr.td | 4 + clang/lib/CodeGen/CGHLSLRuntime.cpp | 98 +++++++++++++++++++ clang/lib/CodeGen/CGHLSLRuntime.h | 12 +++ clang/lib/Sema/SemaHLSL.cpp | 4 +- .../semantics/semantic.arbitrary.hlsl | 32 ++++++ .../CodeGenHLSL/semantics/semantic.array.hlsl | 37 +++++++ .../semantics/semantic.struct.hlsl | 79 +++++++++++++++ .../SemaHLSL/Semantics/semantics-invalid.hlsl | 17 ++++ .../SemaHLSL/Semantics/semantics-valid.hlsl | 42 ++++++++ llvm/include/llvm/IR/IntrinsicsDirectX.td | 6 ++ 10 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 clang/test/CodeGenHLSL/semantics/semantic.arbitrary.hlsl create mode 100644 clang/test/CodeGenHLSL/semantics/semantic.array.hlsl create mode 100644 clang/test/CodeGenHLSL/semantics/semantic.struct.hlsl create mode 100644 clang/test/SemaHLSL/Semantics/semantics-invalid.hlsl create mode 100644 clang/test/SemaHLSL/Semantics/semantics-valid.hlsl diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index bbedc020767a0..d3bec6229fe57 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -4941,6 +4941,10 @@ def HLSLUnparsedSemantic : HLSLAnnotationAttr { let Documentation = [InternalOnly]; } +def HLSLUserSemantic : HLSLSemanticAttr { + let Documentation = [InternalOnly]; +} + def HLSLSV_Position : HLSLSemanticAttr { let Documentation = [HLSLSV_PositionDocs]; } diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index e7ae242f3b90a..f012bc4fafa70 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -371,6 +371,16 @@ static void addSPIRVBuiltinDecoration(llvm::GlobalVariable *GV, GV->addMetadata("spirv.Decorations", *Decoration); } +static void addLocationDecoration(llvm::GlobalVariable *GV, unsigned Location) { + LLVMContext &Ctx = GV->getContext(); + IRBuilder<> B(GV->getContext()); + MDNode *Operands = + MDNode::get(Ctx, {ConstantAsMetadata::get(B.getInt32(/* Location */ 30)), + ConstantAsMetadata::get(B.getInt32(Location))}); + MDNode *Decoration = MDNode::get(Ctx, {Operands}); + GV->addMetadata("spirv.Decorations", *Decoration); +} + static llvm::Value *createSPIRVBuiltinLoad(IRBuilder<> &B, llvm::Module &M, llvm::Type *Ty, const Twine &Name, unsigned BuiltInID) { @@ -384,6 +394,91 @@ static llvm::Value *createSPIRVBuiltinLoad(IRBuilder<> &B, llvm::Module &M, return B.CreateLoad(Ty, GV); } +static llvm::Value *createSPIRVLocationLoad(IRBuilder<> &B, llvm::Module &M, + llvm::Type *Ty, unsigned Location, + StringRef Name = "") { + auto *GV = new llvm::GlobalVariable( + M, Ty, /* isConstant= */ true, llvm::GlobalValue::ExternalLinkage, + /* Initializer= */ nullptr, /* Name= */ Name, /* insertBefore= */ nullptr, + llvm::GlobalVariable::GeneralDynamicTLSModel, + /* AddressSpace */ 7, /* isExternallyInitialized= */ true); + GV->setVisibility(llvm::GlobalValue::HiddenVisibility); + addLocationDecoration(GV, Location); + return B.CreateLoad(Ty, GV); +} + +llvm::Value * +CGHLSLRuntime::emitSPIRVUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, + SemanticInfo &ActiveSemantic) { + Twine BaseName = Twine(ActiveSemantic.Semantic->getAttrName()->getName()); + Twine VariableName = BaseName.concat(Twine(ActiveSemantic.Index)); + + unsigned Location = SPIRVLastAssignedInputSemanticLocation; + + // DXC completely ignores the semantic/index pair. Location are assigned from + // the first semantic to the last. + llvm::ArrayType *AT = dyn_cast(Type); + unsigned ElementCount = AT ? AT->getNumElements() : 1; + SPIRVLastAssignedInputSemanticLocation += ElementCount; + + return createSPIRVLocationLoad(B, CGM.getModule(), Type, Location, + VariableName.str()); + llvm_unreachable("Unsupported target for user-semantic load."); +} + +llvm::Value * +CGHLSLRuntime::emitDXILUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, + SemanticInfo &ActiveSemantic) { + Twine BaseName = Twine(ActiveSemantic.Semantic->getAttrName()->getName()); + Twine VariableName = BaseName.concat(Twine(ActiveSemantic.Index)); + + // DXIL packing rules etc shall be handled here. + // FIXME: generate proper sigpoint, index, col, row values. + // FIXME: also DXIL loads vectors element by element. + SmallVector Args{B.getInt32(4), B.getInt32(0), B.getInt32(0), + B.getInt8(0), + llvm::PoisonValue::get(B.getInt32Ty())}; + + llvm::Intrinsic::ID IntrinsicID = llvm::Intrinsic::dx_load_input; + llvm::Value *Value = B.CreateIntrinsic(/*ReturnType=*/Type, IntrinsicID, Args, + nullptr, VariableName); + return Value; +} + +llvm::Value * +CGHLSLRuntime::emitUserSemanticLoad(IRBuilder<> &B, llvm::Type *Type, + const clang::DeclaratorDecl *Decl, + SemanticInfo &ActiveSemantic) { + uint32_t Location = ActiveSemantic.Index; + + llvm::Value *SemanticValue = nullptr; + if (CGM.getTarget().getTriple().isSPIRV()) + SemanticValue = emitSPIRVUserSemanticLoad(B, Type, ActiveSemantic); + else if (CGM.getTarget().getTriple().isDXIL()) + SemanticValue = emitDXILUserSemanticLoad(B, Type, ActiveSemantic); + else + llvm_unreachable("Unsupported target for user-semantic load."); + + llvm::ArrayType *AT = dyn_cast(Type); + unsigned ElementCount = AT ? AT->getNumElements() : 1; + ActiveSemantic.Index += ElementCount; + + // Mark the semantic/index pair as active and detect collisions. + Twine BaseName = Twine(ActiveSemantic.Semantic->getAttrName()->getName()); + for (unsigned I = 0; I < ElementCount; I++) { + Twine VariableName = BaseName.concat(Twine(Location + I)); + auto [_, Inserted] = ActiveInputSemantics.insert(VariableName.str()); + if (!Inserted) { + CGM.getDiags().Report(Decl->getInnerLocStart(), + diag::err_hlsl_semantic_index_overlap) + << VariableName.str(); + return nullptr; + } + } + + return SemanticValue; +} + llvm::Value * CGHLSLRuntime::emitSystemSemanticLoad(IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl, @@ -451,6 +546,9 @@ CGHLSLRuntime::handleScalarSemanticLoad(IRBuilder<> &B, llvm::Type *Type, ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex(); } + if (auto *UserSemantic = + dyn_cast(ActiveSemantic.Semantic)) + return emitUserSemanticLoad(B, Type, Decl, ActiveSemantic); return emitSystemSemanticLoad(B, Type, Decl, ActiveSemantic); } diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index 93f54e41d524e..9203e18b808d8 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -145,6 +145,16 @@ class CGHLSLRuntime { uint32_t Index; }; + llvm::Value *emitSPIRVUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, + SemanticInfo &ActiveSemantic); + + llvm::Value *emitDXILUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, + SemanticInfo &ActiveSemantic); + + llvm::Value *emitUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, + const clang::DeclaratorDecl *Decl, + SemanticInfo &ActiveSemantic); + llvm::Value *emitSystemSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl, SemanticInfo &ActiveSemantic); @@ -200,6 +210,8 @@ class CGHLSLRuntime { std::unordered_set ActiveInputSemantics; std::unordered_set ActiveOutputSemantics; + + unsigned SPIRVLastAssignedInputSemanticLocation = 0; }; } // namespace CodeGen diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 1617b07fab208..11daf680ffff5 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -858,6 +858,8 @@ void SemaHLSL::CheckSemanticAnnotation( return; DiagnoseAttrStageMismatch(AnnotationAttr, ST, {llvm::Triple::Pixel}); break; + case attr::HLSLUserSemantic: + return; default: llvm_unreachable("Unknown HLSLAnnotationAttr"); } @@ -1581,7 +1583,7 @@ void SemaHLSL::handleSemanticAttr(Decl *D, const ParsedAttr &AL) { if (AL.getAttrName()->getName().starts_with("SV_")) diagnoseSystemSemanticAttr(D, AL, Index); else - Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL; + D->addAttr(createSemanticAttr(AL, Index)); } void SemaHLSL::handlePackOffsetAttr(Decl *D, const ParsedAttr &AL) { diff --git a/clang/test/CodeGenHLSL/semantics/semantic.arbitrary.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.arbitrary.hlsl new file mode 100644 index 0000000000000..220e56b69febc --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/semantic.arbitrary.hlsl @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -triple spirv-unknown-vulkan-vertex -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV -DTARGET=spv +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-vertex -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx + +// CHECK-SPIRV-DAG: @AAA0 = external hidden thread_local addrspace(7) externally_initialized constant float, !spirv.Decorations ![[#METADATA_0:]] +// CHECK-SPIRV-DAG: @B0 = external hidden thread_local addrspace(7) externally_initialized constant i32, !spirv.Decorations ![[#METADATA_2:]] +// CHECK-SPIRV-DAG: @CC0 = external hidden thread_local addrspace(7) externally_initialized constant <2 x float>, !spirv.Decorations ![[#METADATA_4:]] + + +// FIXME: replace `float2 c` with a matrix when available. +void main(float a : AAA, int b : B, float2 c : CC) { + float tmp = a + b + c.x + c.y; +} +// CHECK-SPIRV: define internal spir_func void @_Z4mainfiDv2_f(float noundef nofpclass(nan inf) %a, i32 noundef %b, <2 x float> noundef nofpclass(nan inf) %c) #0 { + +// CHECK: define void @main() + +// CHECK-DXIL: %AAA0 = call float @llvm.dx.load.input.f32(i32 4, i32 0, i32 0, i8 0, i32 poison) +// CHECK-DXIL: %B0 = call i32 @llvm.dx.load.input.i32(i32 4, i32 0, i32 0, i8 0, i32 poison) +// CHECK-DXIL %CC0 = call <2 x float> @llvm.dx.load.input.v2f32(i32 4, i32 0, i32 0, i8 0, i32 poison) +// CHECK-DXIL: call void @_Z4mainfiDv2_f(float %AAA0, i32 %B0, <2 x float> %CC0) + +// CHECK-SPIRV: %[[#AAA0:]] = load float, ptr addrspace(7) @AAA0, align 4 +// CHECK-SPIRV: %[[#B0:]] = load i32, ptr addrspace(7) @B0, align 4 +// CHECK-SPIRV: %[[#CC0:]] = load <2 x float>, ptr addrspace(7) @CC0, align 8 +// CHECK-SPIRV: call spir_func void @_Z4mainfiDv2_f(float %[[#AAA0]], i32 %[[#B0]], <2 x float> %[[#CC0]]) [ "convergencectrl"(token %0) ] + +// CHECK-SPIRV-DAG: ![[#METADATA_0]] = !{![[#METADATA_1:]]} +// CHECK-SPIRV-DAG: ![[#METADATA_1]] = !{i32 30, i32 0} +// CHECK-SPIRV-DAG: ![[#METADATA_2]] = !{![[#METADATA_3:]]} +// CHECK-SPIRV-DAG: ![[#METADATA_3]] = !{i32 30, i32 1} +// CHECK-SPIRV-DAG: ![[#METADATA_4]] = !{![[#METADATA_5:]]} +// CHECK-SPIRV-DAG: ![[#METADATA_5]] = !{i32 30, i32 2} diff --git a/clang/test/CodeGenHLSL/semantics/semantic.array.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.array.hlsl new file mode 100644 index 0000000000000..b2cb3dad9f0ce --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/semantic.array.hlsl @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV -DTARGET=spv +// RUN: %clang_cc1 -triple dxil-px-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx + +struct S0 { + float4 position[2]; + float4 color; +}; + +// CHECK: %struct.S0 = type { [2 x <4 x float>], <4 x float> } + +// CHECK-SPIRV: @A0 = external hidden thread_local addrspace(7) externally_initialized constant [2 x <4 x float>], !spirv.Decorations ![[#MD_0:]] +// CHECK-SPIRV: @A2 = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_2:]] + +// CHECK: define void @main0() +// CHECK-DXIL: %A0 = call [2 x <4 x float>] @llvm.dx.load.input.a2v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison) +// CHECK-DXIL: %[[#TMP0:]] = insertvalue %struct.S0 poison, [2 x <4 x float>] %A0, 0 +// CHECK-DXIL: %A2 = call <4 x float> @llvm.dx.load.input.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison) +// CHECK-DXIL: %[[#TMP1:]] = insertvalue %struct.S0 %[[#TMP0]], <4 x float> %A2, 1 + +// CHECK-SPIRV: %[[#A0:]] = load [2 x <4 x float>], ptr addrspace(7) @A0, align 16 +// CHECK-SPIRV: %[[#TMP0:]] = insertvalue %struct.S0 poison, [2 x <4 x float>] %[[#A0]], 0 +// CHECK-SPIRV: %[[#A2:]] = load <4 x float>, ptr addrspace(7) @A2, align 16 +// CHECK-SPIRV: %[[#TMP1:]] = insertvalue %struct.S0 %[[#TMP0]], <4 x float> %[[#A2]], 1 + +// CHECK: %[[#ARG:]] = alloca %struct.S0, align 16 +// CHECK: store %struct.S0 %[[#TMP1]], ptr %[[#ARG]], align 16 +// CHECK-DXIL: call void @{{.*}}main0{{.*}}(ptr %[[#ARG]]) +// CHECK-SPIRV: call spir_func void @{{.*}}main0{{.*}}(ptr %[[#ARG]]) +[shader("pixel")] +void main0(S0 p : A) { + float tmp = p.position[0] + p.position[1] + p.color; +} + +// CHECK-SPIRV: ![[#MD_0]] = !{![[#MD_1:]]} +// CHECK-SPIRV: ![[#MD_1]] = !{i32 30, i32 0} +// CHECK-SPIRV: ![[#MD_2]] = !{![[#MD_3:]]} +// CHECK-SPIRV: ![[#MD_3]] = !{i32 30, i32 2} diff --git a/clang/test/CodeGenHLSL/semantics/semantic.struct.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.struct.hlsl new file mode 100644 index 0000000000000..1c07941742a4f --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/semantic.struct.hlsl @@ -0,0 +1,79 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx +// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV -DTARGET=spv + +struct S0 { + uint Idx : SV_DispatchThreadID; +}; + +// CHECK: define void @main0() +// CHECK-DXIL: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0) +// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0) +// CHECK: %[[#TMP:]] = insertvalue %struct.S0 poison, i32 %[[#ID:]], 0 +// CHECK: %[[#ARG:]] = alloca %struct.S0, align 8 +// CHECK: store %struct.S0 %[[#TMP]], ptr %[[#ARG]], align 4 +// CHECK-DXIL: call void @{{.*}}main0{{.*}}(ptr %[[#ARG]]) +// CHECK-SPIRV: call spir_func void @{{.*}}main0{{.*}}(ptr %[[#ARG]]) +[shader("compute")] +[numthreads(8,8,1)] +void main0(S0 p) {} + +struct S1 { + uint a : SV_DispatchThreadID; + uint3 b : SV_GroupThreadID; +}; + +// CHECK: define void @main1() +// CHECK-DXIL: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0) +// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0) +// CHECK: %[[#S1A_:]] = insertvalue %struct.S1 poison, i32 %[[#ID:]], 0 +// CHECK-DXIL: %[[#ID_X:]] = call i32 @llvm.[[TARGET]].thread.id.in.group(i32 0) +// CHECK-SPIRV: %[[#ID_X:]] = call i32 @llvm.[[TARGET]].thread.id.in.group.i32(i32 0) +// CHECK: %[[#ID_X_:]] = insertelement <3 x i32> poison, i32 %[[#ID_X]], i64 0 +// CHECK-DXIL: %[[#ID_Y:]] = call i32 @llvm.[[TARGET]].thread.id.in.group(i32 1) +// CHECK-SPIRV: %[[#ID_Y:]] = call i32 @llvm.[[TARGET]].thread.id.in.group.i32(i32 1) +// CHECK: %[[#ID_XY:]] = insertelement <3 x i32> %[[#ID_X_]], i32 %[[#ID_Y]], i64 1 +// CHECK-DXIL: %[[#ID_Z:]] = call i32 @llvm.[[TARGET]].thread.id.in.group(i32 2) +// CHECK-SPIRV: %[[#ID_Z:]] = call i32 @llvm.[[TARGET]].thread.id.in.group.i32(i32 2) +// CHECK: %[[#ID_XYZ:]] = insertelement <3 x i32> %[[#ID_XY]], i32 %[[#ID_Z]], i64 2 +// CHECK: %[[#S1AB:]] = insertvalue %struct.S1 %[[#S1A_]], <3 x i32> %[[#ID_XYZ:]], 1 +// CHECK: %[[#ARG:]] = alloca %struct.S1, align 8 +// CHECK: store %struct.S1 %[[#S1AB]], ptr %[[#ARG]], align 1 +// CHECK-DXIL: call void @{{.*}}main1{{.*}}(ptr %[[#ARG]]) +// CHECK-SPIRV: call spir_func void @{{.*}}main1{{.*}}(ptr %[[#ARG]]) +[shader("compute")] +[numthreads(8,8,1)] +void main1(S1 p) {} + +struct S2C { + uint3 b : SV_GroupThreadID; +}; + +struct S2 { + uint a : SV_DispatchThreadID; + S2C child; +}; + +// CHECK: define void @main2() +// CHECK-DXIL: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0) +// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0) +// CHECK: %[[#S2A_:]] = insertvalue %struct.S2 poison, i32 %[[#ID:]], 0 + +// CHECK-DXIL: %[[#ID_X:]] = call i32 @llvm.[[TARGET]].thread.id.in.group(i32 0) +// CHECK-SPIRV: %[[#ID_X:]] = call i32 @llvm.[[TARGET]].thread.id.in.group.i32(i32 0) +// CHECK: %[[#ID_X_:]] = insertelement <3 x i32> poison, i32 %[[#ID_X]], i64 0 +// CHECK-DXIL: %[[#ID_Y:]] = call i32 @llvm.[[TARGET]].thread.id.in.group(i32 1) +// CHECK-SPIRV: %[[#ID_Y:]] = call i32 @llvm.[[TARGET]].thread.id.in.group.i32(i32 1) +// CHECK: %[[#ID_XY:]] = insertelement <3 x i32> %[[#ID_X_]], i32 %[[#ID_Y]], i64 1 +// CHECK-DXIL: %[[#ID_Z:]] = call i32 @llvm.[[TARGET]].thread.id.in.group(i32 2) +// CHECK-SPIRV: %[[#ID_Z:]] = call i32 @llvm.[[TARGET]].thread.id.in.group.i32(i32 2) +// CHECK: %[[#ID_XYZ:]] = insertelement <3 x i32> %[[#ID_XY]], i32 %[[#ID_Z]], i64 2 +// CHECK: %[[#S2C:]] = insertvalue %struct.S2C poison, <3 x i32> %[[#ID_XYZ:]], 0 + +// CHECK: %[[#S2AB:]] = insertvalue %struct.S2 %[[#S2A_]], %struct.S2C %[[#S2V:]], 1 +// CHECK: %[[#ARG:]] = alloca %struct.S2, align 8 +// CHECK: store %struct.S2 %[[#S2AB]], ptr %[[#ARG]], align 1 +// CHECK-DXIL: call void @{{.*}}main2{{.*}}(ptr %[[#ARG]]) +// CHECK-SPIRV: call spir_func void @{{.*}}main2{{.*}}(ptr %[[#ARG]]) +[shader("compute")] +[numthreads(8,8,1)] +void main2(S2 p) {} diff --git a/clang/test/SemaHLSL/Semantics/semantics-invalid.hlsl b/clang/test/SemaHLSL/Semantics/semantics-invalid.hlsl new file mode 100644 index 0000000000000..fdba6f624d289 --- /dev/null +++ b/clang/test/SemaHLSL/Semantics/semantics-invalid.hlsl @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -fsyntax-only -hlsl-entry main -verify %s + +typedef float t_f : SEMANTIC; // expected-warning{{'SEMANTIC' attribute only applies to parameters, non-static data members, and functions}} + +struct semantic_on_struct : SEMANTIC { // expected-error{{expected class name}} + float a; +}; + +struct s_fields_multiple_semantics { + float a : semantic_a : semantic_c; // expected-error{{use of undeclared identifier 'semantic_c'}} + float b : semantic_b; +}; + +[numthreads(1, 1, 1)] +void main() { + float a : SEM_A; // expected-warning{{'SEM_A' attribute only applies to parameters, non-static data members, and functions}} +} diff --git a/clang/test/SemaHLSL/Semantics/semantics-valid.hlsl b/clang/test/SemaHLSL/Semantics/semantics-valid.hlsl new file mode 100644 index 0000000000000..09fcbae552075 --- /dev/null +++ b/clang/test/SemaHLSL/Semantics/semantics-valid.hlsl @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -hlsl-entry CSMain -x hlsl -finclude-default-header -ast-dump -o - %s | FileCheck %s + +// FIXME: DXC accepts this. +#if 0 +struct semantic_on_struct_instance { + float a; +} g_struct : semantic; + +#endif + + +struct s_fields { + float a : semantic_a; + float b : semantic_b; +// CHECK: |-CXXRecordDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> line:[[@LINE-3]]:8 struct s_fields definition +// CHECK: | |-FieldDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:9 a 'float' +// CHECK: | | `-HLSLUserSemanticAttr 0x{{[0-9a-fA-F]+}} +// CHECK: | `-FieldDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:9 b 'float' +// CHECK: | `-HLSLUserSemanticAttr 0x{{[0-9a-fA-F]+}} +}; + +float fn_foo1(float a : a, float b : b) : sem_ret { return 1.0f; } +// CHECK: |-FunctionDecl {{.*}} <{{.*}}> col:7 fn_foo1 'float (float, float)' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} <{{.*}}> col:21 a 'float' +// CHECK-NEXT: | | `-HLSLUserSemanticAttr {{.*}} <{{.*}}> +// CHECK-NEXT: | |-ParmVarDecl {{.*}} <{{.*}}> col:34 b 'float' +// CHECK-NEXT: | | `-HLSLUserSemanticAttr {{.*}} <{{.*}}> +// CHECK-NEXT: | |-CompoundStmt {{.*}} <{{.*}}> +// CHECK-NEXT: | | `-ReturnStmt {{.*}} <{{.*}}> +// CHECK-NEXT: | | `-FloatingLiteral {{.*}} <{{.*}}> 'float' 1.000000e+00 +// CHECK-NEXT: | `-HLSLUserSemanticAttr {{.*}} <{{.*}}> +float fn_foo2(float a : a, float b : b) : sem_ret : also_ret { return 1.0f; } +// CHECK: `-FunctionDecl {{.*}} <{{.*}}> col:7 fn_foo2 'float (float, float)' +// CHECK-NEXT: |-ParmVarDecl {{.*}} <{{.*}}> col:21 a 'float' +// CHECK-NEXT: | `-HLSLUserSemanticAttr {{.*}} <{{.*}}> +// CHECK-NEXT: |-ParmVarDecl {{.*}} <{{.*}}> col:34 b 'float' +// CHECK-NEXT: | `-HLSLUserSemanticAttr {{.*}} <{{.*}}> +// CHECK-NEXT: |-CompoundStmt {{.*}} <{{.*}}> +// CHECK-NEXT: | `-ReturnStmt {{.*}} <{{.*}}> +// CHECK-NEXT: | `-FloatingLiteral {{.*}} <{{.*}}> 'float' 1.000000e+00 +// CHECK-NEXT: |-HLSLUserSemanticAttr {{.*}} <{{.*}}> +// CHECK-NEXT: `-HLSLUserSemanticAttr {{.*}} <{{.*}}> diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td index 68b31a899003b..d528981ed08ee 100644 --- a/llvm/include/llvm/IR/IntrinsicsDirectX.td +++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td @@ -161,4 +161,10 @@ def int_dx_firstbitshigh : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, def int_dx_firstbitlow : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>; def int_dx_group_memory_barrier_with_group_sync : DefaultAttrsIntrinsic<[], [], []>; + +def int_dx_load_input + : DefaultAttrsIntrinsic<[llvm_any_ty], + [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i8_ty, + llvm_i32_ty], + [IntrConvergent]>; }