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..d3bec6229fe57 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,39 @@ 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 HLSLUserSemantic : HLSLSemanticAttr { + 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 +4977,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..f012bc4fafa70 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" @@ -370,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) { @@ -383,47 +394,210 @@ 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()) { +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, + 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(); + } + + if (auto *UserSemantic = + dyn_cast(ActiveSemantic.Semantic)) + return emitUserSemanticLoad(B, Type, Decl, ActiveSemantic); + 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) { + if (Type->isStructTy()) + return handleStructSemanticLoad(B, Type, Decl, ActiveSemantic); + return handleScalarSemanticLoad(B, Type, Decl, ActiveSemantic); } void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD, @@ -440,13 +614,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 +633,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 +643,34 @@ 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())); + 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); 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..9203e18b808d8 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,40 @@ 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 *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); + + llvm::Value *handleScalarSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, + 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); public: CGHLSLRuntime(CodeGenModule &CGM) : CGM(CGM) {} @@ -172,6 +207,11 @@ class CGHLSLRuntime { llvm::Triple::ArchType getArch(); llvm::DenseMap LayoutTypes; + + std::unordered_set ActiveInputSemantics; + std::unordered_set ActiveOutputSemantics; + + unsigned SPIRVLastAssignedInputSemanticLocation = 0; }; } // 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..11daf680ffff5 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,12 +854,12 @@ 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}); break; + case attr::HLSLUserSemantic: + return; default: llvm_unreachable("Unknown HLSLAnnotationAttr"); } @@ -1492,18 +1508,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 +1519,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 + D->addAttr(createSemanticAttr(AL, Index)); } 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/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) {} 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/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/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/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) { 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]>; }