1782ac218SXiang Li //===- SemaHLSL.cpp - Semantic Analysis for HLSL constructs ---------------===// 2782ac218SXiang Li // 3782ac218SXiang Li // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4782ac218SXiang Li // See https://llvm.org/LICENSE.txt for license information. 5782ac218SXiang Li // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6782ac218SXiang Li // 7782ac218SXiang Li //===----------------------------------------------------------------------===// 8782ac218SXiang Li // This implements Semantic Analysis for HLSL constructs. 9782ac218SXiang Li //===----------------------------------------------------------------------===// 10782ac218SXiang Li 11d345f6a2SVlad Serebrennikov #include "clang/Sema/SemaHLSL.h" 12b45c9c31SVlad Serebrennikov #include "clang/Basic/DiagnosticSema.h" 13b45c9c31SVlad Serebrennikov #include "clang/Basic/LLVM.h" 14b45c9c31SVlad Serebrennikov #include "clang/Basic/TargetInfo.h" 15782ac218SXiang Li #include "clang/Sema/Sema.h" 16b45c9c31SVlad Serebrennikov #include "llvm/ADT/STLExtras.h" 17b45c9c31SVlad Serebrennikov #include "llvm/ADT/StringExtras.h" 18b45c9c31SVlad Serebrennikov #include "llvm/ADT/StringRef.h" 19b45c9c31SVlad Serebrennikov #include "llvm/Support/ErrorHandling.h" 20b45c9c31SVlad Serebrennikov #include "llvm/TargetParser/Triple.h" 21b45c9c31SVlad Serebrennikov #include <iterator> 22782ac218SXiang Li 23782ac218SXiang Li using namespace clang; 24782ac218SXiang Li 25d345f6a2SVlad Serebrennikov SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S) {} 26d345f6a2SVlad Serebrennikov 27b45c9c31SVlad Serebrennikov Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer, 28b45c9c31SVlad Serebrennikov SourceLocation KwLoc, IdentifierInfo *Ident, 29782ac218SXiang Li SourceLocation IdentLoc, 30782ac218SXiang Li SourceLocation LBrace) { 31782ac218SXiang Li // For anonymous namespace, take the location of the left brace. 32d345f6a2SVlad Serebrennikov DeclContext *LexicalParent = SemaRef.getCurLexicalContext(); 33782ac218SXiang Li HLSLBufferDecl *Result = HLSLBufferDecl::Create( 34d345f6a2SVlad Serebrennikov getASTContext(), LexicalParent, CBuffer, KwLoc, Ident, IdentLoc, LBrace); 35782ac218SXiang Li 36d345f6a2SVlad Serebrennikov SemaRef.PushOnScopeChains(Result, BufferScope); 37d345f6a2SVlad Serebrennikov SemaRef.PushDeclContext(BufferScope, Result); 38782ac218SXiang Li 39782ac218SXiang Li return Result; 40782ac218SXiang Li } 41782ac218SXiang Li 42*41f0574cSXiang Li // Calculate the size of a legacy cbuffer type based on 43*41f0574cSXiang Li // https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules 44*41f0574cSXiang Li static unsigned calculateLegacyCbufferSize(const ASTContext &Context, 45*41f0574cSXiang Li QualType T) { 46*41f0574cSXiang Li unsigned Size = 0; 47*41f0574cSXiang Li constexpr unsigned CBufferAlign = 128; 48*41f0574cSXiang Li if (const RecordType *RT = T->getAs<RecordType>()) { 49*41f0574cSXiang Li const RecordDecl *RD = RT->getDecl(); 50*41f0574cSXiang Li for (const FieldDecl *Field : RD->fields()) { 51*41f0574cSXiang Li QualType Ty = Field->getType(); 52*41f0574cSXiang Li unsigned FieldSize = calculateLegacyCbufferSize(Context, Ty); 53*41f0574cSXiang Li unsigned FieldAlign = 32; 54*41f0574cSXiang Li if (Ty->isAggregateType()) 55*41f0574cSXiang Li FieldAlign = CBufferAlign; 56*41f0574cSXiang Li Size = llvm::alignTo(Size, FieldAlign); 57*41f0574cSXiang Li Size += FieldSize; 58*41f0574cSXiang Li } 59*41f0574cSXiang Li } else if (const ConstantArrayType *AT = Context.getAsConstantArrayType(T)) { 60*41f0574cSXiang Li if (unsigned ElementCount = AT->getSize().getZExtValue()) { 61*41f0574cSXiang Li unsigned ElementSize = 62*41f0574cSXiang Li calculateLegacyCbufferSize(Context, AT->getElementType()); 63*41f0574cSXiang Li unsigned AlignedElementSize = llvm::alignTo(ElementSize, CBufferAlign); 64*41f0574cSXiang Li Size = AlignedElementSize * (ElementCount - 1) + ElementSize; 65*41f0574cSXiang Li } 66*41f0574cSXiang Li } else if (const VectorType *VT = T->getAs<VectorType>()) { 67*41f0574cSXiang Li unsigned ElementCount = VT->getNumElements(); 68*41f0574cSXiang Li unsigned ElementSize = 69*41f0574cSXiang Li calculateLegacyCbufferSize(Context, VT->getElementType()); 70*41f0574cSXiang Li Size = ElementSize * ElementCount; 71*41f0574cSXiang Li } else { 72*41f0574cSXiang Li Size = Context.getTypeSize(T); 73*41f0574cSXiang Li } 74*41f0574cSXiang Li return Size; 75*41f0574cSXiang Li } 76*41f0574cSXiang Li 77b45c9c31SVlad Serebrennikov void SemaHLSL::ActOnFinishBuffer(Decl *Dcl, SourceLocation RBrace) { 78782ac218SXiang Li auto *BufDecl = cast<HLSLBufferDecl>(Dcl); 79782ac218SXiang Li BufDecl->setRBraceLoc(RBrace); 80*41f0574cSXiang Li 81*41f0574cSXiang Li // Validate packoffset. 82*41f0574cSXiang Li llvm::SmallVector<std::pair<VarDecl *, HLSLPackOffsetAttr *>> PackOffsetVec; 83*41f0574cSXiang Li bool HasPackOffset = false; 84*41f0574cSXiang Li bool HasNonPackOffset = false; 85*41f0574cSXiang Li for (auto *Field : BufDecl->decls()) { 86*41f0574cSXiang Li VarDecl *Var = dyn_cast<VarDecl>(Field); 87*41f0574cSXiang Li if (!Var) 88*41f0574cSXiang Li continue; 89*41f0574cSXiang Li if (Field->hasAttr<HLSLPackOffsetAttr>()) { 90*41f0574cSXiang Li PackOffsetVec.emplace_back(Var, Field->getAttr<HLSLPackOffsetAttr>()); 91*41f0574cSXiang Li HasPackOffset = true; 92*41f0574cSXiang Li } else { 93*41f0574cSXiang Li HasNonPackOffset = true; 94*41f0574cSXiang Li } 95*41f0574cSXiang Li } 96*41f0574cSXiang Li 97*41f0574cSXiang Li if (HasPackOffset && HasNonPackOffset) 98*41f0574cSXiang Li Diag(BufDecl->getLocation(), diag::warn_hlsl_packoffset_mix); 99*41f0574cSXiang Li 100*41f0574cSXiang Li if (HasPackOffset) { 101*41f0574cSXiang Li ASTContext &Context = getASTContext(); 102*41f0574cSXiang Li // Make sure no overlap in packoffset. 103*41f0574cSXiang Li // Sort PackOffsetVec by offset. 104*41f0574cSXiang Li std::sort(PackOffsetVec.begin(), PackOffsetVec.end(), 105*41f0574cSXiang Li [](const std::pair<VarDecl *, HLSLPackOffsetAttr *> &LHS, 106*41f0574cSXiang Li const std::pair<VarDecl *, HLSLPackOffsetAttr *> &RHS) { 107*41f0574cSXiang Li return LHS.second->getOffset() < RHS.second->getOffset(); 108*41f0574cSXiang Li }); 109*41f0574cSXiang Li 110*41f0574cSXiang Li for (unsigned i = 0; i < PackOffsetVec.size() - 1; i++) { 111*41f0574cSXiang Li VarDecl *Var = PackOffsetVec[i].first; 112*41f0574cSXiang Li HLSLPackOffsetAttr *Attr = PackOffsetVec[i].second; 113*41f0574cSXiang Li unsigned Size = calculateLegacyCbufferSize(Context, Var->getType()); 114*41f0574cSXiang Li unsigned Begin = Attr->getOffset() * 32; 115*41f0574cSXiang Li unsigned End = Begin + Size; 116*41f0574cSXiang Li unsigned NextBegin = PackOffsetVec[i + 1].second->getOffset() * 32; 117*41f0574cSXiang Li if (End > NextBegin) { 118*41f0574cSXiang Li VarDecl *NextVar = PackOffsetVec[i + 1].first; 119*41f0574cSXiang Li Diag(NextVar->getLocation(), diag::err_hlsl_packoffset_overlap) 120*41f0574cSXiang Li << NextVar << Var; 121*41f0574cSXiang Li } 122*41f0574cSXiang Li } 123*41f0574cSXiang Li } 124*41f0574cSXiang Li 125d345f6a2SVlad Serebrennikov SemaRef.PopDeclContext(); 126782ac218SXiang Li } 127b45c9c31SVlad Serebrennikov 128b45c9c31SVlad Serebrennikov HLSLNumThreadsAttr *SemaHLSL::mergeNumThreadsAttr(Decl *D, 129b45c9c31SVlad Serebrennikov const AttributeCommonInfo &AL, 130b45c9c31SVlad Serebrennikov int X, int Y, int Z) { 131b45c9c31SVlad Serebrennikov if (HLSLNumThreadsAttr *NT = D->getAttr<HLSLNumThreadsAttr>()) { 132b45c9c31SVlad Serebrennikov if (NT->getX() != X || NT->getY() != Y || NT->getZ() != Z) { 133b45c9c31SVlad Serebrennikov Diag(NT->getLocation(), diag::err_hlsl_attribute_param_mismatch) << AL; 134b45c9c31SVlad Serebrennikov Diag(AL.getLoc(), diag::note_conflicting_attribute); 135b45c9c31SVlad Serebrennikov } 136b45c9c31SVlad Serebrennikov return nullptr; 137b45c9c31SVlad Serebrennikov } 138b45c9c31SVlad Serebrennikov return ::new (getASTContext()) 139b45c9c31SVlad Serebrennikov HLSLNumThreadsAttr(getASTContext(), AL, X, Y, Z); 140b45c9c31SVlad Serebrennikov } 141b45c9c31SVlad Serebrennikov 142b45c9c31SVlad Serebrennikov HLSLShaderAttr * 143b45c9c31SVlad Serebrennikov SemaHLSL::mergeShaderAttr(Decl *D, const AttributeCommonInfo &AL, 144b45c9c31SVlad Serebrennikov HLSLShaderAttr::ShaderType ShaderType) { 145b45c9c31SVlad Serebrennikov if (HLSLShaderAttr *NT = D->getAttr<HLSLShaderAttr>()) { 146b45c9c31SVlad Serebrennikov if (NT->getType() != ShaderType) { 147b45c9c31SVlad Serebrennikov Diag(NT->getLocation(), diag::err_hlsl_attribute_param_mismatch) << AL; 148b45c9c31SVlad Serebrennikov Diag(AL.getLoc(), diag::note_conflicting_attribute); 149b45c9c31SVlad Serebrennikov } 150b45c9c31SVlad Serebrennikov return nullptr; 151b45c9c31SVlad Serebrennikov } 152b45c9c31SVlad Serebrennikov return HLSLShaderAttr::Create(getASTContext(), ShaderType, AL); 153b45c9c31SVlad Serebrennikov } 154b45c9c31SVlad Serebrennikov 155b45c9c31SVlad Serebrennikov HLSLParamModifierAttr * 156b45c9c31SVlad Serebrennikov SemaHLSL::mergeParamModifierAttr(Decl *D, const AttributeCommonInfo &AL, 157b45c9c31SVlad Serebrennikov HLSLParamModifierAttr::Spelling Spelling) { 158b45c9c31SVlad Serebrennikov // We can only merge an `in` attribute with an `out` attribute. All other 159b45c9c31SVlad Serebrennikov // combinations of duplicated attributes are ill-formed. 160b45c9c31SVlad Serebrennikov if (HLSLParamModifierAttr *PA = D->getAttr<HLSLParamModifierAttr>()) { 161b45c9c31SVlad Serebrennikov if ((PA->isIn() && Spelling == HLSLParamModifierAttr::Keyword_out) || 162b45c9c31SVlad Serebrennikov (PA->isOut() && Spelling == HLSLParamModifierAttr::Keyword_in)) { 163b45c9c31SVlad Serebrennikov D->dropAttr<HLSLParamModifierAttr>(); 164b45c9c31SVlad Serebrennikov SourceRange AdjustedRange = {PA->getLocation(), AL.getRange().getEnd()}; 165b45c9c31SVlad Serebrennikov return HLSLParamModifierAttr::Create( 166b45c9c31SVlad Serebrennikov getASTContext(), /*MergedSpelling=*/true, AdjustedRange, 167b45c9c31SVlad Serebrennikov HLSLParamModifierAttr::Keyword_inout); 168b45c9c31SVlad Serebrennikov } 169b45c9c31SVlad Serebrennikov Diag(AL.getLoc(), diag::err_hlsl_duplicate_parameter_modifier) << AL; 170b45c9c31SVlad Serebrennikov Diag(PA->getLocation(), diag::note_conflicting_attribute); 171b45c9c31SVlad Serebrennikov return nullptr; 172b45c9c31SVlad Serebrennikov } 173b45c9c31SVlad Serebrennikov return HLSLParamModifierAttr::Create(getASTContext(), AL); 174b45c9c31SVlad Serebrennikov } 175b45c9c31SVlad Serebrennikov 176b45c9c31SVlad Serebrennikov void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) { 177b45c9c31SVlad Serebrennikov auto &TargetInfo = getASTContext().getTargetInfo(); 178b45c9c31SVlad Serebrennikov 179b45c9c31SVlad Serebrennikov if (FD->getName() != TargetInfo.getTargetOpts().HLSLEntry) 180b45c9c31SVlad Serebrennikov return; 181b45c9c31SVlad Serebrennikov 182b45c9c31SVlad Serebrennikov StringRef Env = TargetInfo.getTriple().getEnvironmentName(); 183b45c9c31SVlad Serebrennikov HLSLShaderAttr::ShaderType ShaderType; 184b45c9c31SVlad Serebrennikov if (HLSLShaderAttr::ConvertStrToShaderType(Env, ShaderType)) { 185b45c9c31SVlad Serebrennikov if (const auto *Shader = FD->getAttr<HLSLShaderAttr>()) { 186b45c9c31SVlad Serebrennikov // The entry point is already annotated - check that it matches the 187b45c9c31SVlad Serebrennikov // triple. 188b45c9c31SVlad Serebrennikov if (Shader->getType() != ShaderType) { 189b45c9c31SVlad Serebrennikov Diag(Shader->getLocation(), diag::err_hlsl_entry_shader_attr_mismatch) 190b45c9c31SVlad Serebrennikov << Shader; 191b45c9c31SVlad Serebrennikov FD->setInvalidDecl(); 192b45c9c31SVlad Serebrennikov } 193b45c9c31SVlad Serebrennikov } else { 194b45c9c31SVlad Serebrennikov // Implicitly add the shader attribute if the entry function isn't 195b45c9c31SVlad Serebrennikov // explicitly annotated. 196b45c9c31SVlad Serebrennikov FD->addAttr(HLSLShaderAttr::CreateImplicit(getASTContext(), ShaderType, 197b45c9c31SVlad Serebrennikov FD->getBeginLoc())); 198b45c9c31SVlad Serebrennikov } 199b45c9c31SVlad Serebrennikov } else { 200b45c9c31SVlad Serebrennikov switch (TargetInfo.getTriple().getEnvironment()) { 201b45c9c31SVlad Serebrennikov case llvm::Triple::UnknownEnvironment: 202b45c9c31SVlad Serebrennikov case llvm::Triple::Library: 203b45c9c31SVlad Serebrennikov break; 204b45c9c31SVlad Serebrennikov default: 205b45c9c31SVlad Serebrennikov llvm_unreachable("Unhandled environment in triple"); 206b45c9c31SVlad Serebrennikov } 207b45c9c31SVlad Serebrennikov } 208b45c9c31SVlad Serebrennikov } 209b45c9c31SVlad Serebrennikov 210b45c9c31SVlad Serebrennikov void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) { 211b45c9c31SVlad Serebrennikov const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>(); 212b45c9c31SVlad Serebrennikov assert(ShaderAttr && "Entry point has no shader attribute"); 213b45c9c31SVlad Serebrennikov HLSLShaderAttr::ShaderType ST = ShaderAttr->getType(); 214b45c9c31SVlad Serebrennikov 215b45c9c31SVlad Serebrennikov switch (ST) { 216b45c9c31SVlad Serebrennikov case HLSLShaderAttr::Pixel: 217b45c9c31SVlad Serebrennikov case HLSLShaderAttr::Vertex: 218b45c9c31SVlad Serebrennikov case HLSLShaderAttr::Geometry: 219b45c9c31SVlad Serebrennikov case HLSLShaderAttr::Hull: 220b45c9c31SVlad Serebrennikov case HLSLShaderAttr::Domain: 221b45c9c31SVlad Serebrennikov case HLSLShaderAttr::RayGeneration: 222b45c9c31SVlad Serebrennikov case HLSLShaderAttr::Intersection: 223b45c9c31SVlad Serebrennikov case HLSLShaderAttr::AnyHit: 224b45c9c31SVlad Serebrennikov case HLSLShaderAttr::ClosestHit: 225b45c9c31SVlad Serebrennikov case HLSLShaderAttr::Miss: 226b45c9c31SVlad Serebrennikov case HLSLShaderAttr::Callable: 227b45c9c31SVlad Serebrennikov if (const auto *NT = FD->getAttr<HLSLNumThreadsAttr>()) { 228b45c9c31SVlad Serebrennikov DiagnoseAttrStageMismatch(NT, ST, 229b45c9c31SVlad Serebrennikov {HLSLShaderAttr::Compute, 230b45c9c31SVlad Serebrennikov HLSLShaderAttr::Amplification, 231b45c9c31SVlad Serebrennikov HLSLShaderAttr::Mesh}); 232b45c9c31SVlad Serebrennikov FD->setInvalidDecl(); 233b45c9c31SVlad Serebrennikov } 234b45c9c31SVlad Serebrennikov break; 235b45c9c31SVlad Serebrennikov 236b45c9c31SVlad Serebrennikov case HLSLShaderAttr::Compute: 237b45c9c31SVlad Serebrennikov case HLSLShaderAttr::Amplification: 238b45c9c31SVlad Serebrennikov case HLSLShaderAttr::Mesh: 239b45c9c31SVlad Serebrennikov if (!FD->hasAttr<HLSLNumThreadsAttr>()) { 240b45c9c31SVlad Serebrennikov Diag(FD->getLocation(), diag::err_hlsl_missing_numthreads) 241b45c9c31SVlad Serebrennikov << HLSLShaderAttr::ConvertShaderTypeToStr(ST); 242b45c9c31SVlad Serebrennikov FD->setInvalidDecl(); 243b45c9c31SVlad Serebrennikov } 244b45c9c31SVlad Serebrennikov break; 245b45c9c31SVlad Serebrennikov } 246b45c9c31SVlad Serebrennikov 247b45c9c31SVlad Serebrennikov for (ParmVarDecl *Param : FD->parameters()) { 248b45c9c31SVlad Serebrennikov if (const auto *AnnotationAttr = Param->getAttr<HLSLAnnotationAttr>()) { 249b45c9c31SVlad Serebrennikov CheckSemanticAnnotation(FD, Param, AnnotationAttr); 250b45c9c31SVlad Serebrennikov } else { 251b45c9c31SVlad Serebrennikov // FIXME: Handle struct parameters where annotations are on struct fields. 252b45c9c31SVlad Serebrennikov // See: https://github.com/llvm/llvm-project/issues/57875 253b45c9c31SVlad Serebrennikov Diag(FD->getLocation(), diag::err_hlsl_missing_semantic_annotation); 254b45c9c31SVlad Serebrennikov Diag(Param->getLocation(), diag::note_previous_decl) << Param; 255b45c9c31SVlad Serebrennikov FD->setInvalidDecl(); 256b45c9c31SVlad Serebrennikov } 257b45c9c31SVlad Serebrennikov } 258b45c9c31SVlad Serebrennikov // FIXME: Verify return type semantic annotation. 259b45c9c31SVlad Serebrennikov } 260b45c9c31SVlad Serebrennikov 261b45c9c31SVlad Serebrennikov void SemaHLSL::CheckSemanticAnnotation( 262b45c9c31SVlad Serebrennikov FunctionDecl *EntryPoint, const Decl *Param, 263b45c9c31SVlad Serebrennikov const HLSLAnnotationAttr *AnnotationAttr) { 264b45c9c31SVlad Serebrennikov auto *ShaderAttr = EntryPoint->getAttr<HLSLShaderAttr>(); 265b45c9c31SVlad Serebrennikov assert(ShaderAttr && "Entry point has no shader attribute"); 266b45c9c31SVlad Serebrennikov HLSLShaderAttr::ShaderType ST = ShaderAttr->getType(); 267b45c9c31SVlad Serebrennikov 268b45c9c31SVlad Serebrennikov switch (AnnotationAttr->getKind()) { 269b45c9c31SVlad Serebrennikov case attr::HLSLSV_DispatchThreadID: 270b45c9c31SVlad Serebrennikov case attr::HLSLSV_GroupIndex: 271b45c9c31SVlad Serebrennikov if (ST == HLSLShaderAttr::Compute) 272b45c9c31SVlad Serebrennikov return; 273b45c9c31SVlad Serebrennikov DiagnoseAttrStageMismatch(AnnotationAttr, ST, {HLSLShaderAttr::Compute}); 274b45c9c31SVlad Serebrennikov break; 275b45c9c31SVlad Serebrennikov default: 276b45c9c31SVlad Serebrennikov llvm_unreachable("Unknown HLSLAnnotationAttr"); 277b45c9c31SVlad Serebrennikov } 278b45c9c31SVlad Serebrennikov } 279b45c9c31SVlad Serebrennikov 280b45c9c31SVlad Serebrennikov void SemaHLSL::DiagnoseAttrStageMismatch( 281b45c9c31SVlad Serebrennikov const Attr *A, HLSLShaderAttr::ShaderType Stage, 282b45c9c31SVlad Serebrennikov std::initializer_list<HLSLShaderAttr::ShaderType> AllowedStages) { 283b45c9c31SVlad Serebrennikov SmallVector<StringRef, 8> StageStrings; 284b45c9c31SVlad Serebrennikov llvm::transform(AllowedStages, std::back_inserter(StageStrings), 285b45c9c31SVlad Serebrennikov [](HLSLShaderAttr::ShaderType ST) { 286b45c9c31SVlad Serebrennikov return StringRef( 287b45c9c31SVlad Serebrennikov HLSLShaderAttr::ConvertShaderTypeToStr(ST)); 288b45c9c31SVlad Serebrennikov }); 289b45c9c31SVlad Serebrennikov Diag(A->getLoc(), diag::err_hlsl_attr_unsupported_in_stage) 290b45c9c31SVlad Serebrennikov << A << HLSLShaderAttr::ConvertShaderTypeToStr(Stage) 291b45c9c31SVlad Serebrennikov << (AllowedStages.size() != 1) << join(StageStrings, ", "); 292b45c9c31SVlad Serebrennikov } 293