1bdd1243dSDimitry Andric //===- SemaHLSL.cpp - Semantic Analysis for HLSL constructs ---------------===// 2bdd1243dSDimitry Andric // 3bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6bdd1243dSDimitry Andric // 7bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 8bdd1243dSDimitry Andric // This implements Semantic Analysis for HLSL constructs. 9bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 10bdd1243dSDimitry Andric 11*0fca6ea1SDimitry Andric #include "clang/Sema/SemaHLSL.h" 12*0fca6ea1SDimitry Andric #include "clang/AST/Decl.h" 13*0fca6ea1SDimitry Andric #include "clang/AST/Expr.h" 14*0fca6ea1SDimitry Andric #include "clang/AST/RecursiveASTVisitor.h" 15*0fca6ea1SDimitry Andric #include "clang/Basic/DiagnosticSema.h" 16*0fca6ea1SDimitry Andric #include "clang/Basic/LLVM.h" 17*0fca6ea1SDimitry Andric #include "clang/Basic/TargetInfo.h" 18*0fca6ea1SDimitry Andric #include "clang/Sema/ParsedAttr.h" 19bdd1243dSDimitry Andric #include "clang/Sema/Sema.h" 20*0fca6ea1SDimitry Andric #include "llvm/ADT/STLExtras.h" 21*0fca6ea1SDimitry Andric #include "llvm/ADT/StringExtras.h" 22*0fca6ea1SDimitry Andric #include "llvm/ADT/StringRef.h" 23*0fca6ea1SDimitry Andric #include "llvm/Support/Casting.h" 24*0fca6ea1SDimitry Andric #include "llvm/Support/ErrorHandling.h" 25*0fca6ea1SDimitry Andric #include "llvm/TargetParser/Triple.h" 26*0fca6ea1SDimitry Andric #include <iterator> 27bdd1243dSDimitry Andric 28bdd1243dSDimitry Andric using namespace clang; 29bdd1243dSDimitry Andric 30*0fca6ea1SDimitry Andric SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S) {} 31*0fca6ea1SDimitry Andric 32*0fca6ea1SDimitry Andric Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer, 33bdd1243dSDimitry Andric SourceLocation KwLoc, IdentifierInfo *Ident, 34bdd1243dSDimitry Andric SourceLocation IdentLoc, 35bdd1243dSDimitry Andric SourceLocation LBrace) { 36bdd1243dSDimitry Andric // For anonymous namespace, take the location of the left brace. 37*0fca6ea1SDimitry Andric DeclContext *LexicalParent = SemaRef.getCurLexicalContext(); 38bdd1243dSDimitry Andric HLSLBufferDecl *Result = HLSLBufferDecl::Create( 39*0fca6ea1SDimitry Andric getASTContext(), LexicalParent, CBuffer, KwLoc, Ident, IdentLoc, LBrace); 40bdd1243dSDimitry Andric 41*0fca6ea1SDimitry Andric SemaRef.PushOnScopeChains(Result, BufferScope); 42*0fca6ea1SDimitry Andric SemaRef.PushDeclContext(BufferScope, Result); 43bdd1243dSDimitry Andric 44bdd1243dSDimitry Andric return Result; 45bdd1243dSDimitry Andric } 46bdd1243dSDimitry Andric 47*0fca6ea1SDimitry Andric // Calculate the size of a legacy cbuffer type based on 48*0fca6ea1SDimitry Andric // https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules 49*0fca6ea1SDimitry Andric static unsigned calculateLegacyCbufferSize(const ASTContext &Context, 50*0fca6ea1SDimitry Andric QualType T) { 51*0fca6ea1SDimitry Andric unsigned Size = 0; 52*0fca6ea1SDimitry Andric constexpr unsigned CBufferAlign = 128; 53*0fca6ea1SDimitry Andric if (const RecordType *RT = T->getAs<RecordType>()) { 54*0fca6ea1SDimitry Andric const RecordDecl *RD = RT->getDecl(); 55*0fca6ea1SDimitry Andric for (const FieldDecl *Field : RD->fields()) { 56*0fca6ea1SDimitry Andric QualType Ty = Field->getType(); 57*0fca6ea1SDimitry Andric unsigned FieldSize = calculateLegacyCbufferSize(Context, Ty); 58*0fca6ea1SDimitry Andric unsigned FieldAlign = 32; 59*0fca6ea1SDimitry Andric if (Ty->isAggregateType()) 60*0fca6ea1SDimitry Andric FieldAlign = CBufferAlign; 61*0fca6ea1SDimitry Andric Size = llvm::alignTo(Size, FieldAlign); 62*0fca6ea1SDimitry Andric Size += FieldSize; 63*0fca6ea1SDimitry Andric } 64*0fca6ea1SDimitry Andric } else if (const ConstantArrayType *AT = Context.getAsConstantArrayType(T)) { 65*0fca6ea1SDimitry Andric if (unsigned ElementCount = AT->getSize().getZExtValue()) { 66*0fca6ea1SDimitry Andric unsigned ElementSize = 67*0fca6ea1SDimitry Andric calculateLegacyCbufferSize(Context, AT->getElementType()); 68*0fca6ea1SDimitry Andric unsigned AlignedElementSize = llvm::alignTo(ElementSize, CBufferAlign); 69*0fca6ea1SDimitry Andric Size = AlignedElementSize * (ElementCount - 1) + ElementSize; 70*0fca6ea1SDimitry Andric } 71*0fca6ea1SDimitry Andric } else if (const VectorType *VT = T->getAs<VectorType>()) { 72*0fca6ea1SDimitry Andric unsigned ElementCount = VT->getNumElements(); 73*0fca6ea1SDimitry Andric unsigned ElementSize = 74*0fca6ea1SDimitry Andric calculateLegacyCbufferSize(Context, VT->getElementType()); 75*0fca6ea1SDimitry Andric Size = ElementSize * ElementCount; 76*0fca6ea1SDimitry Andric } else { 77*0fca6ea1SDimitry Andric Size = Context.getTypeSize(T); 78*0fca6ea1SDimitry Andric } 79*0fca6ea1SDimitry Andric return Size; 80*0fca6ea1SDimitry Andric } 81*0fca6ea1SDimitry Andric 82*0fca6ea1SDimitry Andric void SemaHLSL::ActOnFinishBuffer(Decl *Dcl, SourceLocation RBrace) { 83bdd1243dSDimitry Andric auto *BufDecl = cast<HLSLBufferDecl>(Dcl); 84bdd1243dSDimitry Andric BufDecl->setRBraceLoc(RBrace); 85*0fca6ea1SDimitry Andric 86*0fca6ea1SDimitry Andric // Validate packoffset. 87*0fca6ea1SDimitry Andric llvm::SmallVector<std::pair<VarDecl *, HLSLPackOffsetAttr *>> PackOffsetVec; 88*0fca6ea1SDimitry Andric bool HasPackOffset = false; 89*0fca6ea1SDimitry Andric bool HasNonPackOffset = false; 90*0fca6ea1SDimitry Andric for (auto *Field : BufDecl->decls()) { 91*0fca6ea1SDimitry Andric VarDecl *Var = dyn_cast<VarDecl>(Field); 92*0fca6ea1SDimitry Andric if (!Var) 93*0fca6ea1SDimitry Andric continue; 94*0fca6ea1SDimitry Andric if (Field->hasAttr<HLSLPackOffsetAttr>()) { 95*0fca6ea1SDimitry Andric PackOffsetVec.emplace_back(Var, Field->getAttr<HLSLPackOffsetAttr>()); 96*0fca6ea1SDimitry Andric HasPackOffset = true; 97*0fca6ea1SDimitry Andric } else { 98*0fca6ea1SDimitry Andric HasNonPackOffset = true; 99*0fca6ea1SDimitry Andric } 100*0fca6ea1SDimitry Andric } 101*0fca6ea1SDimitry Andric 102*0fca6ea1SDimitry Andric if (HasPackOffset && HasNonPackOffset) 103*0fca6ea1SDimitry Andric Diag(BufDecl->getLocation(), diag::warn_hlsl_packoffset_mix); 104*0fca6ea1SDimitry Andric 105*0fca6ea1SDimitry Andric if (HasPackOffset) { 106*0fca6ea1SDimitry Andric ASTContext &Context = getASTContext(); 107*0fca6ea1SDimitry Andric // Make sure no overlap in packoffset. 108*0fca6ea1SDimitry Andric // Sort PackOffsetVec by offset. 109*0fca6ea1SDimitry Andric std::sort(PackOffsetVec.begin(), PackOffsetVec.end(), 110*0fca6ea1SDimitry Andric [](const std::pair<VarDecl *, HLSLPackOffsetAttr *> &LHS, 111*0fca6ea1SDimitry Andric const std::pair<VarDecl *, HLSLPackOffsetAttr *> &RHS) { 112*0fca6ea1SDimitry Andric return LHS.second->getOffset() < RHS.second->getOffset(); 113*0fca6ea1SDimitry Andric }); 114*0fca6ea1SDimitry Andric 115*0fca6ea1SDimitry Andric for (unsigned i = 0; i < PackOffsetVec.size() - 1; i++) { 116*0fca6ea1SDimitry Andric VarDecl *Var = PackOffsetVec[i].first; 117*0fca6ea1SDimitry Andric HLSLPackOffsetAttr *Attr = PackOffsetVec[i].second; 118*0fca6ea1SDimitry Andric unsigned Size = calculateLegacyCbufferSize(Context, Var->getType()); 119*0fca6ea1SDimitry Andric unsigned Begin = Attr->getOffset() * 32; 120*0fca6ea1SDimitry Andric unsigned End = Begin + Size; 121*0fca6ea1SDimitry Andric unsigned NextBegin = PackOffsetVec[i + 1].second->getOffset() * 32; 122*0fca6ea1SDimitry Andric if (End > NextBegin) { 123*0fca6ea1SDimitry Andric VarDecl *NextVar = PackOffsetVec[i + 1].first; 124*0fca6ea1SDimitry Andric Diag(NextVar->getLocation(), diag::err_hlsl_packoffset_overlap) 125*0fca6ea1SDimitry Andric << NextVar << Var; 126*0fca6ea1SDimitry Andric } 127*0fca6ea1SDimitry Andric } 128*0fca6ea1SDimitry Andric } 129*0fca6ea1SDimitry Andric 130*0fca6ea1SDimitry Andric SemaRef.PopDeclContext(); 131*0fca6ea1SDimitry Andric } 132*0fca6ea1SDimitry Andric 133*0fca6ea1SDimitry Andric HLSLNumThreadsAttr *SemaHLSL::mergeNumThreadsAttr(Decl *D, 134*0fca6ea1SDimitry Andric const AttributeCommonInfo &AL, 135*0fca6ea1SDimitry Andric int X, int Y, int Z) { 136*0fca6ea1SDimitry Andric if (HLSLNumThreadsAttr *NT = D->getAttr<HLSLNumThreadsAttr>()) { 137*0fca6ea1SDimitry Andric if (NT->getX() != X || NT->getY() != Y || NT->getZ() != Z) { 138*0fca6ea1SDimitry Andric Diag(NT->getLocation(), diag::err_hlsl_attribute_param_mismatch) << AL; 139*0fca6ea1SDimitry Andric Diag(AL.getLoc(), diag::note_conflicting_attribute); 140*0fca6ea1SDimitry Andric } 141*0fca6ea1SDimitry Andric return nullptr; 142*0fca6ea1SDimitry Andric } 143*0fca6ea1SDimitry Andric return ::new (getASTContext()) 144*0fca6ea1SDimitry Andric HLSLNumThreadsAttr(getASTContext(), AL, X, Y, Z); 145*0fca6ea1SDimitry Andric } 146*0fca6ea1SDimitry Andric 147*0fca6ea1SDimitry Andric HLSLShaderAttr * 148*0fca6ea1SDimitry Andric SemaHLSL::mergeShaderAttr(Decl *D, const AttributeCommonInfo &AL, 149*0fca6ea1SDimitry Andric llvm::Triple::EnvironmentType ShaderType) { 150*0fca6ea1SDimitry Andric if (HLSLShaderAttr *NT = D->getAttr<HLSLShaderAttr>()) { 151*0fca6ea1SDimitry Andric if (NT->getType() != ShaderType) { 152*0fca6ea1SDimitry Andric Diag(NT->getLocation(), diag::err_hlsl_attribute_param_mismatch) << AL; 153*0fca6ea1SDimitry Andric Diag(AL.getLoc(), diag::note_conflicting_attribute); 154*0fca6ea1SDimitry Andric } 155*0fca6ea1SDimitry Andric return nullptr; 156*0fca6ea1SDimitry Andric } 157*0fca6ea1SDimitry Andric return HLSLShaderAttr::Create(getASTContext(), ShaderType, AL); 158*0fca6ea1SDimitry Andric } 159*0fca6ea1SDimitry Andric 160*0fca6ea1SDimitry Andric HLSLParamModifierAttr * 161*0fca6ea1SDimitry Andric SemaHLSL::mergeParamModifierAttr(Decl *D, const AttributeCommonInfo &AL, 162*0fca6ea1SDimitry Andric HLSLParamModifierAttr::Spelling Spelling) { 163*0fca6ea1SDimitry Andric // We can only merge an `in` attribute with an `out` attribute. All other 164*0fca6ea1SDimitry Andric // combinations of duplicated attributes are ill-formed. 165*0fca6ea1SDimitry Andric if (HLSLParamModifierAttr *PA = D->getAttr<HLSLParamModifierAttr>()) { 166*0fca6ea1SDimitry Andric if ((PA->isIn() && Spelling == HLSLParamModifierAttr::Keyword_out) || 167*0fca6ea1SDimitry Andric (PA->isOut() && Spelling == HLSLParamModifierAttr::Keyword_in)) { 168*0fca6ea1SDimitry Andric D->dropAttr<HLSLParamModifierAttr>(); 169*0fca6ea1SDimitry Andric SourceRange AdjustedRange = {PA->getLocation(), AL.getRange().getEnd()}; 170*0fca6ea1SDimitry Andric return HLSLParamModifierAttr::Create( 171*0fca6ea1SDimitry Andric getASTContext(), /*MergedSpelling=*/true, AdjustedRange, 172*0fca6ea1SDimitry Andric HLSLParamModifierAttr::Keyword_inout); 173*0fca6ea1SDimitry Andric } 174*0fca6ea1SDimitry Andric Diag(AL.getLoc(), diag::err_hlsl_duplicate_parameter_modifier) << AL; 175*0fca6ea1SDimitry Andric Diag(PA->getLocation(), diag::note_conflicting_attribute); 176*0fca6ea1SDimitry Andric return nullptr; 177*0fca6ea1SDimitry Andric } 178*0fca6ea1SDimitry Andric return HLSLParamModifierAttr::Create(getASTContext(), AL); 179*0fca6ea1SDimitry Andric } 180*0fca6ea1SDimitry Andric 181*0fca6ea1SDimitry Andric void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) { 182*0fca6ea1SDimitry Andric auto &TargetInfo = getASTContext().getTargetInfo(); 183*0fca6ea1SDimitry Andric 184*0fca6ea1SDimitry Andric if (FD->getName() != TargetInfo.getTargetOpts().HLSLEntry) 185*0fca6ea1SDimitry Andric return; 186*0fca6ea1SDimitry Andric 187*0fca6ea1SDimitry Andric llvm::Triple::EnvironmentType Env = TargetInfo.getTriple().getEnvironment(); 188*0fca6ea1SDimitry Andric if (HLSLShaderAttr::isValidShaderType(Env) && Env != llvm::Triple::Library) { 189*0fca6ea1SDimitry Andric if (const auto *Shader = FD->getAttr<HLSLShaderAttr>()) { 190*0fca6ea1SDimitry Andric // The entry point is already annotated - check that it matches the 191*0fca6ea1SDimitry Andric // triple. 192*0fca6ea1SDimitry Andric if (Shader->getType() != Env) { 193*0fca6ea1SDimitry Andric Diag(Shader->getLocation(), diag::err_hlsl_entry_shader_attr_mismatch) 194*0fca6ea1SDimitry Andric << Shader; 195*0fca6ea1SDimitry Andric FD->setInvalidDecl(); 196*0fca6ea1SDimitry Andric } 197*0fca6ea1SDimitry Andric } else { 198*0fca6ea1SDimitry Andric // Implicitly add the shader attribute if the entry function isn't 199*0fca6ea1SDimitry Andric // explicitly annotated. 200*0fca6ea1SDimitry Andric FD->addAttr(HLSLShaderAttr::CreateImplicit(getASTContext(), Env, 201*0fca6ea1SDimitry Andric FD->getBeginLoc())); 202*0fca6ea1SDimitry Andric } 203*0fca6ea1SDimitry Andric } else { 204*0fca6ea1SDimitry Andric switch (Env) { 205*0fca6ea1SDimitry Andric case llvm::Triple::UnknownEnvironment: 206*0fca6ea1SDimitry Andric case llvm::Triple::Library: 207*0fca6ea1SDimitry Andric break; 208*0fca6ea1SDimitry Andric default: 209*0fca6ea1SDimitry Andric llvm_unreachable("Unhandled environment in triple"); 210*0fca6ea1SDimitry Andric } 211*0fca6ea1SDimitry Andric } 212*0fca6ea1SDimitry Andric } 213*0fca6ea1SDimitry Andric 214*0fca6ea1SDimitry Andric void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) { 215*0fca6ea1SDimitry Andric const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>(); 216*0fca6ea1SDimitry Andric assert(ShaderAttr && "Entry point has no shader attribute"); 217*0fca6ea1SDimitry Andric llvm::Triple::EnvironmentType ST = ShaderAttr->getType(); 218*0fca6ea1SDimitry Andric 219*0fca6ea1SDimitry Andric switch (ST) { 220*0fca6ea1SDimitry Andric case llvm::Triple::Pixel: 221*0fca6ea1SDimitry Andric case llvm::Triple::Vertex: 222*0fca6ea1SDimitry Andric case llvm::Triple::Geometry: 223*0fca6ea1SDimitry Andric case llvm::Triple::Hull: 224*0fca6ea1SDimitry Andric case llvm::Triple::Domain: 225*0fca6ea1SDimitry Andric case llvm::Triple::RayGeneration: 226*0fca6ea1SDimitry Andric case llvm::Triple::Intersection: 227*0fca6ea1SDimitry Andric case llvm::Triple::AnyHit: 228*0fca6ea1SDimitry Andric case llvm::Triple::ClosestHit: 229*0fca6ea1SDimitry Andric case llvm::Triple::Miss: 230*0fca6ea1SDimitry Andric case llvm::Triple::Callable: 231*0fca6ea1SDimitry Andric if (const auto *NT = FD->getAttr<HLSLNumThreadsAttr>()) { 232*0fca6ea1SDimitry Andric DiagnoseAttrStageMismatch(NT, ST, 233*0fca6ea1SDimitry Andric {llvm::Triple::Compute, 234*0fca6ea1SDimitry Andric llvm::Triple::Amplification, 235*0fca6ea1SDimitry Andric llvm::Triple::Mesh}); 236*0fca6ea1SDimitry Andric FD->setInvalidDecl(); 237*0fca6ea1SDimitry Andric } 238*0fca6ea1SDimitry Andric break; 239*0fca6ea1SDimitry Andric 240*0fca6ea1SDimitry Andric case llvm::Triple::Compute: 241*0fca6ea1SDimitry Andric case llvm::Triple::Amplification: 242*0fca6ea1SDimitry Andric case llvm::Triple::Mesh: 243*0fca6ea1SDimitry Andric if (!FD->hasAttr<HLSLNumThreadsAttr>()) { 244*0fca6ea1SDimitry Andric Diag(FD->getLocation(), diag::err_hlsl_missing_numthreads) 245*0fca6ea1SDimitry Andric << llvm::Triple::getEnvironmentTypeName(ST); 246*0fca6ea1SDimitry Andric FD->setInvalidDecl(); 247*0fca6ea1SDimitry Andric } 248*0fca6ea1SDimitry Andric break; 249*0fca6ea1SDimitry Andric default: 250*0fca6ea1SDimitry Andric llvm_unreachable("Unhandled environment in triple"); 251*0fca6ea1SDimitry Andric } 252*0fca6ea1SDimitry Andric 253*0fca6ea1SDimitry Andric for (ParmVarDecl *Param : FD->parameters()) { 254*0fca6ea1SDimitry Andric if (const auto *AnnotationAttr = Param->getAttr<HLSLAnnotationAttr>()) { 255*0fca6ea1SDimitry Andric CheckSemanticAnnotation(FD, Param, AnnotationAttr); 256*0fca6ea1SDimitry Andric } else { 257*0fca6ea1SDimitry Andric // FIXME: Handle struct parameters where annotations are on struct fields. 258*0fca6ea1SDimitry Andric // See: https://github.com/llvm/llvm-project/issues/57875 259*0fca6ea1SDimitry Andric Diag(FD->getLocation(), diag::err_hlsl_missing_semantic_annotation); 260*0fca6ea1SDimitry Andric Diag(Param->getLocation(), diag::note_previous_decl) << Param; 261*0fca6ea1SDimitry Andric FD->setInvalidDecl(); 262*0fca6ea1SDimitry Andric } 263*0fca6ea1SDimitry Andric } 264*0fca6ea1SDimitry Andric // FIXME: Verify return type semantic annotation. 265*0fca6ea1SDimitry Andric } 266*0fca6ea1SDimitry Andric 267*0fca6ea1SDimitry Andric void SemaHLSL::CheckSemanticAnnotation( 268*0fca6ea1SDimitry Andric FunctionDecl *EntryPoint, const Decl *Param, 269*0fca6ea1SDimitry Andric const HLSLAnnotationAttr *AnnotationAttr) { 270*0fca6ea1SDimitry Andric auto *ShaderAttr = EntryPoint->getAttr<HLSLShaderAttr>(); 271*0fca6ea1SDimitry Andric assert(ShaderAttr && "Entry point has no shader attribute"); 272*0fca6ea1SDimitry Andric llvm::Triple::EnvironmentType ST = ShaderAttr->getType(); 273*0fca6ea1SDimitry Andric 274*0fca6ea1SDimitry Andric switch (AnnotationAttr->getKind()) { 275*0fca6ea1SDimitry Andric case attr::HLSLSV_DispatchThreadID: 276*0fca6ea1SDimitry Andric case attr::HLSLSV_GroupIndex: 277*0fca6ea1SDimitry Andric if (ST == llvm::Triple::Compute) 278*0fca6ea1SDimitry Andric return; 279*0fca6ea1SDimitry Andric DiagnoseAttrStageMismatch(AnnotationAttr, ST, {llvm::Triple::Compute}); 280*0fca6ea1SDimitry Andric break; 281*0fca6ea1SDimitry Andric default: 282*0fca6ea1SDimitry Andric llvm_unreachable("Unknown HLSLAnnotationAttr"); 283*0fca6ea1SDimitry Andric } 284*0fca6ea1SDimitry Andric } 285*0fca6ea1SDimitry Andric 286*0fca6ea1SDimitry Andric void SemaHLSL::DiagnoseAttrStageMismatch( 287*0fca6ea1SDimitry Andric const Attr *A, llvm::Triple::EnvironmentType Stage, 288*0fca6ea1SDimitry Andric std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages) { 289*0fca6ea1SDimitry Andric SmallVector<StringRef, 8> StageStrings; 290*0fca6ea1SDimitry Andric llvm::transform(AllowedStages, std::back_inserter(StageStrings), 291*0fca6ea1SDimitry Andric [](llvm::Triple::EnvironmentType ST) { 292*0fca6ea1SDimitry Andric return StringRef( 293*0fca6ea1SDimitry Andric HLSLShaderAttr::ConvertEnvironmentTypeToStr(ST)); 294*0fca6ea1SDimitry Andric }); 295*0fca6ea1SDimitry Andric Diag(A->getLoc(), diag::err_hlsl_attr_unsupported_in_stage) 296*0fca6ea1SDimitry Andric << A << llvm::Triple::getEnvironmentTypeName(Stage) 297*0fca6ea1SDimitry Andric << (AllowedStages.size() != 1) << join(StageStrings, ", "); 298*0fca6ea1SDimitry Andric } 299*0fca6ea1SDimitry Andric 300*0fca6ea1SDimitry Andric void SemaHLSL::handleNumThreadsAttr(Decl *D, const ParsedAttr &AL) { 301*0fca6ea1SDimitry Andric llvm::VersionTuple SMVersion = 302*0fca6ea1SDimitry Andric getASTContext().getTargetInfo().getTriple().getOSVersion(); 303*0fca6ea1SDimitry Andric uint32_t ZMax = 1024; 304*0fca6ea1SDimitry Andric uint32_t ThreadMax = 1024; 305*0fca6ea1SDimitry Andric if (SMVersion.getMajor() <= 4) { 306*0fca6ea1SDimitry Andric ZMax = 1; 307*0fca6ea1SDimitry Andric ThreadMax = 768; 308*0fca6ea1SDimitry Andric } else if (SMVersion.getMajor() == 5) { 309*0fca6ea1SDimitry Andric ZMax = 64; 310*0fca6ea1SDimitry Andric ThreadMax = 1024; 311*0fca6ea1SDimitry Andric } 312*0fca6ea1SDimitry Andric 313*0fca6ea1SDimitry Andric uint32_t X; 314*0fca6ea1SDimitry Andric if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), X)) 315*0fca6ea1SDimitry Andric return; 316*0fca6ea1SDimitry Andric if (X > 1024) { 317*0fca6ea1SDimitry Andric Diag(AL.getArgAsExpr(0)->getExprLoc(), 318*0fca6ea1SDimitry Andric diag::err_hlsl_numthreads_argument_oor) 319*0fca6ea1SDimitry Andric << 0 << 1024; 320*0fca6ea1SDimitry Andric return; 321*0fca6ea1SDimitry Andric } 322*0fca6ea1SDimitry Andric uint32_t Y; 323*0fca6ea1SDimitry Andric if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(1), Y)) 324*0fca6ea1SDimitry Andric return; 325*0fca6ea1SDimitry Andric if (Y > 1024) { 326*0fca6ea1SDimitry Andric Diag(AL.getArgAsExpr(1)->getExprLoc(), 327*0fca6ea1SDimitry Andric diag::err_hlsl_numthreads_argument_oor) 328*0fca6ea1SDimitry Andric << 1 << 1024; 329*0fca6ea1SDimitry Andric return; 330*0fca6ea1SDimitry Andric } 331*0fca6ea1SDimitry Andric uint32_t Z; 332*0fca6ea1SDimitry Andric if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(2), Z)) 333*0fca6ea1SDimitry Andric return; 334*0fca6ea1SDimitry Andric if (Z > ZMax) { 335*0fca6ea1SDimitry Andric SemaRef.Diag(AL.getArgAsExpr(2)->getExprLoc(), 336*0fca6ea1SDimitry Andric diag::err_hlsl_numthreads_argument_oor) 337*0fca6ea1SDimitry Andric << 2 << ZMax; 338*0fca6ea1SDimitry Andric return; 339*0fca6ea1SDimitry Andric } 340*0fca6ea1SDimitry Andric 341*0fca6ea1SDimitry Andric if (X * Y * Z > ThreadMax) { 342*0fca6ea1SDimitry Andric Diag(AL.getLoc(), diag::err_hlsl_numthreads_invalid) << ThreadMax; 343*0fca6ea1SDimitry Andric return; 344*0fca6ea1SDimitry Andric } 345*0fca6ea1SDimitry Andric 346*0fca6ea1SDimitry Andric HLSLNumThreadsAttr *NewAttr = mergeNumThreadsAttr(D, AL, X, Y, Z); 347*0fca6ea1SDimitry Andric if (NewAttr) 348*0fca6ea1SDimitry Andric D->addAttr(NewAttr); 349*0fca6ea1SDimitry Andric } 350*0fca6ea1SDimitry Andric 351*0fca6ea1SDimitry Andric static bool isLegalTypeForHLSLSV_DispatchThreadID(QualType T) { 352*0fca6ea1SDimitry Andric if (!T->hasUnsignedIntegerRepresentation()) 353*0fca6ea1SDimitry Andric return false; 354*0fca6ea1SDimitry Andric if (const auto *VT = T->getAs<VectorType>()) 355*0fca6ea1SDimitry Andric return VT->getNumElements() <= 3; 356*0fca6ea1SDimitry Andric return true; 357*0fca6ea1SDimitry Andric } 358*0fca6ea1SDimitry Andric 359*0fca6ea1SDimitry Andric void SemaHLSL::handleSV_DispatchThreadIDAttr(Decl *D, const ParsedAttr &AL) { 360*0fca6ea1SDimitry Andric auto *VD = cast<ValueDecl>(D); 361*0fca6ea1SDimitry Andric if (!isLegalTypeForHLSLSV_DispatchThreadID(VD->getType())) { 362*0fca6ea1SDimitry Andric Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type) 363*0fca6ea1SDimitry Andric << AL << "uint/uint2/uint3"; 364*0fca6ea1SDimitry Andric return; 365*0fca6ea1SDimitry Andric } 366*0fca6ea1SDimitry Andric 367*0fca6ea1SDimitry Andric D->addAttr(::new (getASTContext()) 368*0fca6ea1SDimitry Andric HLSLSV_DispatchThreadIDAttr(getASTContext(), AL)); 369*0fca6ea1SDimitry Andric } 370*0fca6ea1SDimitry Andric 371*0fca6ea1SDimitry Andric void SemaHLSL::handlePackOffsetAttr(Decl *D, const ParsedAttr &AL) { 372*0fca6ea1SDimitry Andric if (!isa<VarDecl>(D) || !isa<HLSLBufferDecl>(D->getDeclContext())) { 373*0fca6ea1SDimitry Andric Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_ast_node) 374*0fca6ea1SDimitry Andric << AL << "shader constant in a constant buffer"; 375*0fca6ea1SDimitry Andric return; 376*0fca6ea1SDimitry Andric } 377*0fca6ea1SDimitry Andric 378*0fca6ea1SDimitry Andric uint32_t SubComponent; 379*0fca6ea1SDimitry Andric if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), SubComponent)) 380*0fca6ea1SDimitry Andric return; 381*0fca6ea1SDimitry Andric uint32_t Component; 382*0fca6ea1SDimitry Andric if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(1), Component)) 383*0fca6ea1SDimitry Andric return; 384*0fca6ea1SDimitry Andric 385*0fca6ea1SDimitry Andric QualType T = cast<VarDecl>(D)->getType().getCanonicalType(); 386*0fca6ea1SDimitry Andric // Check if T is an array or struct type. 387*0fca6ea1SDimitry Andric // TODO: mark matrix type as aggregate type. 388*0fca6ea1SDimitry Andric bool IsAggregateTy = (T->isArrayType() || T->isStructureType()); 389*0fca6ea1SDimitry Andric 390*0fca6ea1SDimitry Andric // Check Component is valid for T. 391*0fca6ea1SDimitry Andric if (Component) { 392*0fca6ea1SDimitry Andric unsigned Size = getASTContext().getTypeSize(T); 393*0fca6ea1SDimitry Andric if (IsAggregateTy || Size > 128) { 394*0fca6ea1SDimitry Andric Diag(AL.getLoc(), diag::err_hlsl_packoffset_cross_reg_boundary); 395*0fca6ea1SDimitry Andric return; 396*0fca6ea1SDimitry Andric } else { 397*0fca6ea1SDimitry Andric // Make sure Component + sizeof(T) <= 4. 398*0fca6ea1SDimitry Andric if ((Component * 32 + Size) > 128) { 399*0fca6ea1SDimitry Andric Diag(AL.getLoc(), diag::err_hlsl_packoffset_cross_reg_boundary); 400*0fca6ea1SDimitry Andric return; 401*0fca6ea1SDimitry Andric } 402*0fca6ea1SDimitry Andric QualType EltTy = T; 403*0fca6ea1SDimitry Andric if (const auto *VT = T->getAs<VectorType>()) 404*0fca6ea1SDimitry Andric EltTy = VT->getElementType(); 405*0fca6ea1SDimitry Andric unsigned Align = getASTContext().getTypeAlign(EltTy); 406*0fca6ea1SDimitry Andric if (Align > 32 && Component == 1) { 407*0fca6ea1SDimitry Andric // NOTE: Component 3 will hit err_hlsl_packoffset_cross_reg_boundary. 408*0fca6ea1SDimitry Andric // So we only need to check Component 1 here. 409*0fca6ea1SDimitry Andric Diag(AL.getLoc(), diag::err_hlsl_packoffset_alignment_mismatch) 410*0fca6ea1SDimitry Andric << Align << EltTy; 411*0fca6ea1SDimitry Andric return; 412*0fca6ea1SDimitry Andric } 413*0fca6ea1SDimitry Andric } 414*0fca6ea1SDimitry Andric } 415*0fca6ea1SDimitry Andric 416*0fca6ea1SDimitry Andric D->addAttr(::new (getASTContext()) HLSLPackOffsetAttr( 417*0fca6ea1SDimitry Andric getASTContext(), AL, SubComponent, Component)); 418*0fca6ea1SDimitry Andric } 419*0fca6ea1SDimitry Andric 420*0fca6ea1SDimitry Andric void SemaHLSL::handleShaderAttr(Decl *D, const ParsedAttr &AL) { 421*0fca6ea1SDimitry Andric StringRef Str; 422*0fca6ea1SDimitry Andric SourceLocation ArgLoc; 423*0fca6ea1SDimitry Andric if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc)) 424*0fca6ea1SDimitry Andric return; 425*0fca6ea1SDimitry Andric 426*0fca6ea1SDimitry Andric llvm::Triple::EnvironmentType ShaderType; 427*0fca6ea1SDimitry Andric if (!HLSLShaderAttr::ConvertStrToEnvironmentType(Str, ShaderType)) { 428*0fca6ea1SDimitry Andric Diag(AL.getLoc(), diag::warn_attribute_type_not_supported) 429*0fca6ea1SDimitry Andric << AL << Str << ArgLoc; 430*0fca6ea1SDimitry Andric return; 431*0fca6ea1SDimitry Andric } 432*0fca6ea1SDimitry Andric 433*0fca6ea1SDimitry Andric // FIXME: check function match the shader stage. 434*0fca6ea1SDimitry Andric 435*0fca6ea1SDimitry Andric HLSLShaderAttr *NewAttr = mergeShaderAttr(D, AL, ShaderType); 436*0fca6ea1SDimitry Andric if (NewAttr) 437*0fca6ea1SDimitry Andric D->addAttr(NewAttr); 438*0fca6ea1SDimitry Andric } 439*0fca6ea1SDimitry Andric 440*0fca6ea1SDimitry Andric void SemaHLSL::handleResourceClassAttr(Decl *D, const ParsedAttr &AL) { 441*0fca6ea1SDimitry Andric if (!AL.isArgIdent(0)) { 442*0fca6ea1SDimitry Andric Diag(AL.getLoc(), diag::err_attribute_argument_type) 443*0fca6ea1SDimitry Andric << AL << AANT_ArgumentIdentifier; 444*0fca6ea1SDimitry Andric return; 445*0fca6ea1SDimitry Andric } 446*0fca6ea1SDimitry Andric 447*0fca6ea1SDimitry Andric IdentifierLoc *Loc = AL.getArgAsIdent(0); 448*0fca6ea1SDimitry Andric StringRef Identifier = Loc->Ident->getName(); 449*0fca6ea1SDimitry Andric SourceLocation ArgLoc = Loc->Loc; 450*0fca6ea1SDimitry Andric 451*0fca6ea1SDimitry Andric // Validate. 452*0fca6ea1SDimitry Andric llvm::dxil::ResourceClass RC; 453*0fca6ea1SDimitry Andric if (!HLSLResourceClassAttr::ConvertStrToResourceClass(Identifier, RC)) { 454*0fca6ea1SDimitry Andric Diag(ArgLoc, diag::warn_attribute_type_not_supported) 455*0fca6ea1SDimitry Andric << "ResourceClass" << Identifier; 456*0fca6ea1SDimitry Andric return; 457*0fca6ea1SDimitry Andric } 458*0fca6ea1SDimitry Andric 459*0fca6ea1SDimitry Andric D->addAttr(HLSLResourceClassAttr::Create(getASTContext(), RC, ArgLoc)); 460*0fca6ea1SDimitry Andric } 461*0fca6ea1SDimitry Andric 462*0fca6ea1SDimitry Andric void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) { 463*0fca6ea1SDimitry Andric StringRef Space = "space0"; 464*0fca6ea1SDimitry Andric StringRef Slot = ""; 465*0fca6ea1SDimitry Andric 466*0fca6ea1SDimitry Andric if (!AL.isArgIdent(0)) { 467*0fca6ea1SDimitry Andric Diag(AL.getLoc(), diag::err_attribute_argument_type) 468*0fca6ea1SDimitry Andric << AL << AANT_ArgumentIdentifier; 469*0fca6ea1SDimitry Andric return; 470*0fca6ea1SDimitry Andric } 471*0fca6ea1SDimitry Andric 472*0fca6ea1SDimitry Andric IdentifierLoc *Loc = AL.getArgAsIdent(0); 473*0fca6ea1SDimitry Andric StringRef Str = Loc->Ident->getName(); 474*0fca6ea1SDimitry Andric SourceLocation ArgLoc = Loc->Loc; 475*0fca6ea1SDimitry Andric 476*0fca6ea1SDimitry Andric SourceLocation SpaceArgLoc; 477*0fca6ea1SDimitry Andric if (AL.getNumArgs() == 2) { 478*0fca6ea1SDimitry Andric Slot = Str; 479*0fca6ea1SDimitry Andric if (!AL.isArgIdent(1)) { 480*0fca6ea1SDimitry Andric Diag(AL.getLoc(), diag::err_attribute_argument_type) 481*0fca6ea1SDimitry Andric << AL << AANT_ArgumentIdentifier; 482*0fca6ea1SDimitry Andric return; 483*0fca6ea1SDimitry Andric } 484*0fca6ea1SDimitry Andric 485*0fca6ea1SDimitry Andric IdentifierLoc *Loc = AL.getArgAsIdent(1); 486*0fca6ea1SDimitry Andric Space = Loc->Ident->getName(); 487*0fca6ea1SDimitry Andric SpaceArgLoc = Loc->Loc; 488*0fca6ea1SDimitry Andric } else { 489*0fca6ea1SDimitry Andric Slot = Str; 490*0fca6ea1SDimitry Andric } 491*0fca6ea1SDimitry Andric 492*0fca6ea1SDimitry Andric // Validate. 493*0fca6ea1SDimitry Andric if (!Slot.empty()) { 494*0fca6ea1SDimitry Andric switch (Slot[0]) { 495*0fca6ea1SDimitry Andric case 'u': 496*0fca6ea1SDimitry Andric case 'b': 497*0fca6ea1SDimitry Andric case 's': 498*0fca6ea1SDimitry Andric case 't': 499*0fca6ea1SDimitry Andric break; 500*0fca6ea1SDimitry Andric default: 501*0fca6ea1SDimitry Andric Diag(ArgLoc, diag::err_hlsl_unsupported_register_type) 502*0fca6ea1SDimitry Andric << Slot.substr(0, 1); 503*0fca6ea1SDimitry Andric return; 504*0fca6ea1SDimitry Andric } 505*0fca6ea1SDimitry Andric 506*0fca6ea1SDimitry Andric StringRef SlotNum = Slot.substr(1); 507*0fca6ea1SDimitry Andric unsigned Num = 0; 508*0fca6ea1SDimitry Andric if (SlotNum.getAsInteger(10, Num)) { 509*0fca6ea1SDimitry Andric Diag(ArgLoc, diag::err_hlsl_unsupported_register_number); 510*0fca6ea1SDimitry Andric return; 511*0fca6ea1SDimitry Andric } 512*0fca6ea1SDimitry Andric } 513*0fca6ea1SDimitry Andric 514*0fca6ea1SDimitry Andric if (!Space.starts_with("space")) { 515*0fca6ea1SDimitry Andric Diag(SpaceArgLoc, diag::err_hlsl_expected_space) << Space; 516*0fca6ea1SDimitry Andric return; 517*0fca6ea1SDimitry Andric } 518*0fca6ea1SDimitry Andric StringRef SpaceNum = Space.substr(5); 519*0fca6ea1SDimitry Andric unsigned Num = 0; 520*0fca6ea1SDimitry Andric if (SpaceNum.getAsInteger(10, Num)) { 521*0fca6ea1SDimitry Andric Diag(SpaceArgLoc, diag::err_hlsl_expected_space) << Space; 522*0fca6ea1SDimitry Andric return; 523*0fca6ea1SDimitry Andric } 524*0fca6ea1SDimitry Andric 525*0fca6ea1SDimitry Andric // FIXME: check reg type match decl. Issue 526*0fca6ea1SDimitry Andric // https://github.com/llvm/llvm-project/issues/57886. 527*0fca6ea1SDimitry Andric HLSLResourceBindingAttr *NewAttr = 528*0fca6ea1SDimitry Andric HLSLResourceBindingAttr::Create(getASTContext(), Slot, Space, AL); 529*0fca6ea1SDimitry Andric if (NewAttr) 530*0fca6ea1SDimitry Andric D->addAttr(NewAttr); 531*0fca6ea1SDimitry Andric } 532*0fca6ea1SDimitry Andric 533*0fca6ea1SDimitry Andric void SemaHLSL::handleParamModifierAttr(Decl *D, const ParsedAttr &AL) { 534*0fca6ea1SDimitry Andric HLSLParamModifierAttr *NewAttr = mergeParamModifierAttr( 535*0fca6ea1SDimitry Andric D, AL, 536*0fca6ea1SDimitry Andric static_cast<HLSLParamModifierAttr::Spelling>(AL.getSemanticSpelling())); 537*0fca6ea1SDimitry Andric if (NewAttr) 538*0fca6ea1SDimitry Andric D->addAttr(NewAttr); 539*0fca6ea1SDimitry Andric } 540*0fca6ea1SDimitry Andric 541*0fca6ea1SDimitry Andric namespace { 542*0fca6ea1SDimitry Andric 543*0fca6ea1SDimitry Andric /// This class implements HLSL availability diagnostics for default 544*0fca6ea1SDimitry Andric /// and relaxed mode 545*0fca6ea1SDimitry Andric /// 546*0fca6ea1SDimitry Andric /// The goal of this diagnostic is to emit an error or warning when an 547*0fca6ea1SDimitry Andric /// unavailable API is found in code that is reachable from the shader 548*0fca6ea1SDimitry Andric /// entry function or from an exported function (when compiling a shader 549*0fca6ea1SDimitry Andric /// library). 550*0fca6ea1SDimitry Andric /// 551*0fca6ea1SDimitry Andric /// This is done by traversing the AST of all shader entry point functions 552*0fca6ea1SDimitry Andric /// and of all exported functions, and any functions that are referenced 553*0fca6ea1SDimitry Andric /// from this AST. In other words, any functions that are reachable from 554*0fca6ea1SDimitry Andric /// the entry points. 555*0fca6ea1SDimitry Andric class DiagnoseHLSLAvailability 556*0fca6ea1SDimitry Andric : public RecursiveASTVisitor<DiagnoseHLSLAvailability> { 557*0fca6ea1SDimitry Andric 558*0fca6ea1SDimitry Andric Sema &SemaRef; 559*0fca6ea1SDimitry Andric 560*0fca6ea1SDimitry Andric // Stack of functions to be scaned 561*0fca6ea1SDimitry Andric llvm::SmallVector<const FunctionDecl *, 8> DeclsToScan; 562*0fca6ea1SDimitry Andric 563*0fca6ea1SDimitry Andric // Tracks which environments functions have been scanned in. 564*0fca6ea1SDimitry Andric // 565*0fca6ea1SDimitry Andric // Maps FunctionDecl to an unsigned number that represents the set of shader 566*0fca6ea1SDimitry Andric // environments the function has been scanned for. 567*0fca6ea1SDimitry Andric // The llvm::Triple::EnvironmentType enum values for shader stages guaranteed 568*0fca6ea1SDimitry Andric // to be numbered from llvm::Triple::Pixel to llvm::Triple::Amplification 569*0fca6ea1SDimitry Andric // (verified by static_asserts in Triple.cpp), we can use it to index 570*0fca6ea1SDimitry Andric // individual bits in the set, as long as we shift the values to start with 0 571*0fca6ea1SDimitry Andric // by subtracting the value of llvm::Triple::Pixel first. 572*0fca6ea1SDimitry Andric // 573*0fca6ea1SDimitry Andric // The N'th bit in the set will be set if the function has been scanned 574*0fca6ea1SDimitry Andric // in shader environment whose llvm::Triple::EnvironmentType integer value 575*0fca6ea1SDimitry Andric // equals (llvm::Triple::Pixel + N). 576*0fca6ea1SDimitry Andric // 577*0fca6ea1SDimitry Andric // For example, if a function has been scanned in compute and pixel stage 578*0fca6ea1SDimitry Andric // environment, the value will be 0x21 (100001 binary) because: 579*0fca6ea1SDimitry Andric // 580*0fca6ea1SDimitry Andric // (int)(llvm::Triple::Pixel - llvm::Triple::Pixel) == 0 581*0fca6ea1SDimitry Andric // (int)(llvm::Triple::Compute - llvm::Triple::Pixel) == 5 582*0fca6ea1SDimitry Andric // 583*0fca6ea1SDimitry Andric // A FunctionDecl is mapped to 0 (or not included in the map) if it has not 584*0fca6ea1SDimitry Andric // been scanned in any environment. 585*0fca6ea1SDimitry Andric llvm::DenseMap<const FunctionDecl *, unsigned> ScannedDecls; 586*0fca6ea1SDimitry Andric 587*0fca6ea1SDimitry Andric // Do not access these directly, use the get/set methods below to make 588*0fca6ea1SDimitry Andric // sure the values are in sync 589*0fca6ea1SDimitry Andric llvm::Triple::EnvironmentType CurrentShaderEnvironment; 590*0fca6ea1SDimitry Andric unsigned CurrentShaderStageBit; 591*0fca6ea1SDimitry Andric 592*0fca6ea1SDimitry Andric // True if scanning a function that was already scanned in a different 593*0fca6ea1SDimitry Andric // shader stage context, and therefore we should not report issues that 594*0fca6ea1SDimitry Andric // depend only on shader model version because they would be duplicate. 595*0fca6ea1SDimitry Andric bool ReportOnlyShaderStageIssues; 596*0fca6ea1SDimitry Andric 597*0fca6ea1SDimitry Andric // Helper methods for dealing with current stage context / environment 598*0fca6ea1SDimitry Andric void SetShaderStageContext(llvm::Triple::EnvironmentType ShaderType) { 599*0fca6ea1SDimitry Andric static_assert(sizeof(unsigned) >= 4); 600*0fca6ea1SDimitry Andric assert(HLSLShaderAttr::isValidShaderType(ShaderType)); 601*0fca6ea1SDimitry Andric assert((unsigned)(ShaderType - llvm::Triple::Pixel) < 31 && 602*0fca6ea1SDimitry Andric "ShaderType is too big for this bitmap"); // 31 is reserved for 603*0fca6ea1SDimitry Andric // "unknown" 604*0fca6ea1SDimitry Andric 605*0fca6ea1SDimitry Andric unsigned bitmapIndex = ShaderType - llvm::Triple::Pixel; 606*0fca6ea1SDimitry Andric CurrentShaderEnvironment = ShaderType; 607*0fca6ea1SDimitry Andric CurrentShaderStageBit = (1 << bitmapIndex); 608*0fca6ea1SDimitry Andric } 609*0fca6ea1SDimitry Andric 610*0fca6ea1SDimitry Andric void SetUnknownShaderStageContext() { 611*0fca6ea1SDimitry Andric CurrentShaderEnvironment = llvm::Triple::UnknownEnvironment; 612*0fca6ea1SDimitry Andric CurrentShaderStageBit = (1 << 31); 613*0fca6ea1SDimitry Andric } 614*0fca6ea1SDimitry Andric 615*0fca6ea1SDimitry Andric llvm::Triple::EnvironmentType GetCurrentShaderEnvironment() const { 616*0fca6ea1SDimitry Andric return CurrentShaderEnvironment; 617*0fca6ea1SDimitry Andric } 618*0fca6ea1SDimitry Andric 619*0fca6ea1SDimitry Andric bool InUnknownShaderStageContext() const { 620*0fca6ea1SDimitry Andric return CurrentShaderEnvironment == llvm::Triple::UnknownEnvironment; 621*0fca6ea1SDimitry Andric } 622*0fca6ea1SDimitry Andric 623*0fca6ea1SDimitry Andric // Helper methods for dealing with shader stage bitmap 624*0fca6ea1SDimitry Andric void AddToScannedFunctions(const FunctionDecl *FD) { 625*0fca6ea1SDimitry Andric unsigned &ScannedStages = ScannedDecls.getOrInsertDefault(FD); 626*0fca6ea1SDimitry Andric ScannedStages |= CurrentShaderStageBit; 627*0fca6ea1SDimitry Andric } 628*0fca6ea1SDimitry Andric 629*0fca6ea1SDimitry Andric unsigned GetScannedStages(const FunctionDecl *FD) { 630*0fca6ea1SDimitry Andric return ScannedDecls.getOrInsertDefault(FD); 631*0fca6ea1SDimitry Andric } 632*0fca6ea1SDimitry Andric 633*0fca6ea1SDimitry Andric bool WasAlreadyScannedInCurrentStage(const FunctionDecl *FD) { 634*0fca6ea1SDimitry Andric return WasAlreadyScannedInCurrentStage(GetScannedStages(FD)); 635*0fca6ea1SDimitry Andric } 636*0fca6ea1SDimitry Andric 637*0fca6ea1SDimitry Andric bool WasAlreadyScannedInCurrentStage(unsigned ScannerStages) { 638*0fca6ea1SDimitry Andric return ScannerStages & CurrentShaderStageBit; 639*0fca6ea1SDimitry Andric } 640*0fca6ea1SDimitry Andric 641*0fca6ea1SDimitry Andric static bool NeverBeenScanned(unsigned ScannedStages) { 642*0fca6ea1SDimitry Andric return ScannedStages == 0; 643*0fca6ea1SDimitry Andric } 644*0fca6ea1SDimitry Andric 645*0fca6ea1SDimitry Andric // Scanning methods 646*0fca6ea1SDimitry Andric void HandleFunctionOrMethodRef(FunctionDecl *FD, Expr *RefExpr); 647*0fca6ea1SDimitry Andric void CheckDeclAvailability(NamedDecl *D, const AvailabilityAttr *AA, 648*0fca6ea1SDimitry Andric SourceRange Range); 649*0fca6ea1SDimitry Andric const AvailabilityAttr *FindAvailabilityAttr(const Decl *D); 650*0fca6ea1SDimitry Andric bool HasMatchingEnvironmentOrNone(const AvailabilityAttr *AA); 651*0fca6ea1SDimitry Andric 652*0fca6ea1SDimitry Andric public: 653*0fca6ea1SDimitry Andric DiagnoseHLSLAvailability(Sema &SemaRef) : SemaRef(SemaRef) {} 654*0fca6ea1SDimitry Andric 655*0fca6ea1SDimitry Andric // AST traversal methods 656*0fca6ea1SDimitry Andric void RunOnTranslationUnit(const TranslationUnitDecl *TU); 657*0fca6ea1SDimitry Andric void RunOnFunction(const FunctionDecl *FD); 658*0fca6ea1SDimitry Andric 659*0fca6ea1SDimitry Andric bool VisitDeclRefExpr(DeclRefExpr *DRE) { 660*0fca6ea1SDimitry Andric FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(DRE->getDecl()); 661*0fca6ea1SDimitry Andric if (FD) 662*0fca6ea1SDimitry Andric HandleFunctionOrMethodRef(FD, DRE); 663*0fca6ea1SDimitry Andric return true; 664*0fca6ea1SDimitry Andric } 665*0fca6ea1SDimitry Andric 666*0fca6ea1SDimitry Andric bool VisitMemberExpr(MemberExpr *ME) { 667*0fca6ea1SDimitry Andric FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(ME->getMemberDecl()); 668*0fca6ea1SDimitry Andric if (FD) 669*0fca6ea1SDimitry Andric HandleFunctionOrMethodRef(FD, ME); 670*0fca6ea1SDimitry Andric return true; 671*0fca6ea1SDimitry Andric } 672*0fca6ea1SDimitry Andric }; 673*0fca6ea1SDimitry Andric 674*0fca6ea1SDimitry Andric void DiagnoseHLSLAvailability::HandleFunctionOrMethodRef(FunctionDecl *FD, 675*0fca6ea1SDimitry Andric Expr *RefExpr) { 676*0fca6ea1SDimitry Andric assert((isa<DeclRefExpr>(RefExpr) || isa<MemberExpr>(RefExpr)) && 677*0fca6ea1SDimitry Andric "expected DeclRefExpr or MemberExpr"); 678*0fca6ea1SDimitry Andric 679*0fca6ea1SDimitry Andric // has a definition -> add to stack to be scanned 680*0fca6ea1SDimitry Andric const FunctionDecl *FDWithBody = nullptr; 681*0fca6ea1SDimitry Andric if (FD->hasBody(FDWithBody)) { 682*0fca6ea1SDimitry Andric if (!WasAlreadyScannedInCurrentStage(FDWithBody)) 683*0fca6ea1SDimitry Andric DeclsToScan.push_back(FDWithBody); 684*0fca6ea1SDimitry Andric return; 685*0fca6ea1SDimitry Andric } 686*0fca6ea1SDimitry Andric 687*0fca6ea1SDimitry Andric // no body -> diagnose availability 688*0fca6ea1SDimitry Andric const AvailabilityAttr *AA = FindAvailabilityAttr(FD); 689*0fca6ea1SDimitry Andric if (AA) 690*0fca6ea1SDimitry Andric CheckDeclAvailability( 691*0fca6ea1SDimitry Andric FD, AA, SourceRange(RefExpr->getBeginLoc(), RefExpr->getEndLoc())); 692*0fca6ea1SDimitry Andric } 693*0fca6ea1SDimitry Andric 694*0fca6ea1SDimitry Andric void DiagnoseHLSLAvailability::RunOnTranslationUnit( 695*0fca6ea1SDimitry Andric const TranslationUnitDecl *TU) { 696*0fca6ea1SDimitry Andric 697*0fca6ea1SDimitry Andric // Iterate over all shader entry functions and library exports, and for those 698*0fca6ea1SDimitry Andric // that have a body (definiton), run diag scan on each, setting appropriate 699*0fca6ea1SDimitry Andric // shader environment context based on whether it is a shader entry function 700*0fca6ea1SDimitry Andric // or an exported function. Exported functions can be in namespaces and in 701*0fca6ea1SDimitry Andric // export declarations so we need to scan those declaration contexts as well. 702*0fca6ea1SDimitry Andric llvm::SmallVector<const DeclContext *, 8> DeclContextsToScan; 703*0fca6ea1SDimitry Andric DeclContextsToScan.push_back(TU); 704*0fca6ea1SDimitry Andric 705*0fca6ea1SDimitry Andric while (!DeclContextsToScan.empty()) { 706*0fca6ea1SDimitry Andric const DeclContext *DC = DeclContextsToScan.pop_back_val(); 707*0fca6ea1SDimitry Andric for (auto &D : DC->decls()) { 708*0fca6ea1SDimitry Andric // do not scan implicit declaration generated by the implementation 709*0fca6ea1SDimitry Andric if (D->isImplicit()) 710*0fca6ea1SDimitry Andric continue; 711*0fca6ea1SDimitry Andric 712*0fca6ea1SDimitry Andric // for namespace or export declaration add the context to the list to be 713*0fca6ea1SDimitry Andric // scanned later 714*0fca6ea1SDimitry Andric if (llvm::dyn_cast<NamespaceDecl>(D) || llvm::dyn_cast<ExportDecl>(D)) { 715*0fca6ea1SDimitry Andric DeclContextsToScan.push_back(llvm::dyn_cast<DeclContext>(D)); 716*0fca6ea1SDimitry Andric continue; 717*0fca6ea1SDimitry Andric } 718*0fca6ea1SDimitry Andric 719*0fca6ea1SDimitry Andric // skip over other decls or function decls without body 720*0fca6ea1SDimitry Andric const FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(D); 721*0fca6ea1SDimitry Andric if (!FD || !FD->isThisDeclarationADefinition()) 722*0fca6ea1SDimitry Andric continue; 723*0fca6ea1SDimitry Andric 724*0fca6ea1SDimitry Andric // shader entry point 725*0fca6ea1SDimitry Andric if (HLSLShaderAttr *ShaderAttr = FD->getAttr<HLSLShaderAttr>()) { 726*0fca6ea1SDimitry Andric SetShaderStageContext(ShaderAttr->getType()); 727*0fca6ea1SDimitry Andric RunOnFunction(FD); 728*0fca6ea1SDimitry Andric continue; 729*0fca6ea1SDimitry Andric } 730*0fca6ea1SDimitry Andric // exported library function 731*0fca6ea1SDimitry Andric // FIXME: replace this loop with external linkage check once issue #92071 732*0fca6ea1SDimitry Andric // is resolved 733*0fca6ea1SDimitry Andric bool isExport = FD->isInExportDeclContext(); 734*0fca6ea1SDimitry Andric if (!isExport) { 735*0fca6ea1SDimitry Andric for (const auto *Redecl : FD->redecls()) { 736*0fca6ea1SDimitry Andric if (Redecl->isInExportDeclContext()) { 737*0fca6ea1SDimitry Andric isExport = true; 738*0fca6ea1SDimitry Andric break; 739*0fca6ea1SDimitry Andric } 740*0fca6ea1SDimitry Andric } 741*0fca6ea1SDimitry Andric } 742*0fca6ea1SDimitry Andric if (isExport) { 743*0fca6ea1SDimitry Andric SetUnknownShaderStageContext(); 744*0fca6ea1SDimitry Andric RunOnFunction(FD); 745*0fca6ea1SDimitry Andric continue; 746*0fca6ea1SDimitry Andric } 747*0fca6ea1SDimitry Andric } 748*0fca6ea1SDimitry Andric } 749*0fca6ea1SDimitry Andric } 750*0fca6ea1SDimitry Andric 751*0fca6ea1SDimitry Andric void DiagnoseHLSLAvailability::RunOnFunction(const FunctionDecl *FD) { 752*0fca6ea1SDimitry Andric assert(DeclsToScan.empty() && "DeclsToScan should be empty"); 753*0fca6ea1SDimitry Andric DeclsToScan.push_back(FD); 754*0fca6ea1SDimitry Andric 755*0fca6ea1SDimitry Andric while (!DeclsToScan.empty()) { 756*0fca6ea1SDimitry Andric // Take one decl from the stack and check it by traversing its AST. 757*0fca6ea1SDimitry Andric // For any CallExpr found during the traversal add it's callee to the top of 758*0fca6ea1SDimitry Andric // the stack to be processed next. Functions already processed are stored in 759*0fca6ea1SDimitry Andric // ScannedDecls. 760*0fca6ea1SDimitry Andric const FunctionDecl *FD = DeclsToScan.pop_back_val(); 761*0fca6ea1SDimitry Andric 762*0fca6ea1SDimitry Andric // Decl was already scanned 763*0fca6ea1SDimitry Andric const unsigned ScannedStages = GetScannedStages(FD); 764*0fca6ea1SDimitry Andric if (WasAlreadyScannedInCurrentStage(ScannedStages)) 765*0fca6ea1SDimitry Andric continue; 766*0fca6ea1SDimitry Andric 767*0fca6ea1SDimitry Andric ReportOnlyShaderStageIssues = !NeverBeenScanned(ScannedStages); 768*0fca6ea1SDimitry Andric 769*0fca6ea1SDimitry Andric AddToScannedFunctions(FD); 770*0fca6ea1SDimitry Andric TraverseStmt(FD->getBody()); 771*0fca6ea1SDimitry Andric } 772*0fca6ea1SDimitry Andric } 773*0fca6ea1SDimitry Andric 774*0fca6ea1SDimitry Andric bool DiagnoseHLSLAvailability::HasMatchingEnvironmentOrNone( 775*0fca6ea1SDimitry Andric const AvailabilityAttr *AA) { 776*0fca6ea1SDimitry Andric IdentifierInfo *IIEnvironment = AA->getEnvironment(); 777*0fca6ea1SDimitry Andric if (!IIEnvironment) 778*0fca6ea1SDimitry Andric return true; 779*0fca6ea1SDimitry Andric 780*0fca6ea1SDimitry Andric llvm::Triple::EnvironmentType CurrentEnv = GetCurrentShaderEnvironment(); 781*0fca6ea1SDimitry Andric if (CurrentEnv == llvm::Triple::UnknownEnvironment) 782*0fca6ea1SDimitry Andric return false; 783*0fca6ea1SDimitry Andric 784*0fca6ea1SDimitry Andric llvm::Triple::EnvironmentType AttrEnv = 785*0fca6ea1SDimitry Andric AvailabilityAttr::getEnvironmentType(IIEnvironment->getName()); 786*0fca6ea1SDimitry Andric 787*0fca6ea1SDimitry Andric return CurrentEnv == AttrEnv; 788*0fca6ea1SDimitry Andric } 789*0fca6ea1SDimitry Andric 790*0fca6ea1SDimitry Andric const AvailabilityAttr * 791*0fca6ea1SDimitry Andric DiagnoseHLSLAvailability::FindAvailabilityAttr(const Decl *D) { 792*0fca6ea1SDimitry Andric AvailabilityAttr const *PartialMatch = nullptr; 793*0fca6ea1SDimitry Andric // Check each AvailabilityAttr to find the one for this platform. 794*0fca6ea1SDimitry Andric // For multiple attributes with the same platform try to find one for this 795*0fca6ea1SDimitry Andric // environment. 796*0fca6ea1SDimitry Andric for (const auto *A : D->attrs()) { 797*0fca6ea1SDimitry Andric if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) { 798*0fca6ea1SDimitry Andric StringRef AttrPlatform = Avail->getPlatform()->getName(); 799*0fca6ea1SDimitry Andric StringRef TargetPlatform = 800*0fca6ea1SDimitry Andric SemaRef.getASTContext().getTargetInfo().getPlatformName(); 801*0fca6ea1SDimitry Andric 802*0fca6ea1SDimitry Andric // Match the platform name. 803*0fca6ea1SDimitry Andric if (AttrPlatform == TargetPlatform) { 804*0fca6ea1SDimitry Andric // Find the best matching attribute for this environment 805*0fca6ea1SDimitry Andric if (HasMatchingEnvironmentOrNone(Avail)) 806*0fca6ea1SDimitry Andric return Avail; 807*0fca6ea1SDimitry Andric PartialMatch = Avail; 808*0fca6ea1SDimitry Andric } 809*0fca6ea1SDimitry Andric } 810*0fca6ea1SDimitry Andric } 811*0fca6ea1SDimitry Andric return PartialMatch; 812*0fca6ea1SDimitry Andric } 813*0fca6ea1SDimitry Andric 814*0fca6ea1SDimitry Andric // Check availability against target shader model version and current shader 815*0fca6ea1SDimitry Andric // stage and emit diagnostic 816*0fca6ea1SDimitry Andric void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D, 817*0fca6ea1SDimitry Andric const AvailabilityAttr *AA, 818*0fca6ea1SDimitry Andric SourceRange Range) { 819*0fca6ea1SDimitry Andric 820*0fca6ea1SDimitry Andric IdentifierInfo *IIEnv = AA->getEnvironment(); 821*0fca6ea1SDimitry Andric 822*0fca6ea1SDimitry Andric if (!IIEnv) { 823*0fca6ea1SDimitry Andric // The availability attribute does not have environment -> it depends only 824*0fca6ea1SDimitry Andric // on shader model version and not on specific the shader stage. 825*0fca6ea1SDimitry Andric 826*0fca6ea1SDimitry Andric // Skip emitting the diagnostics if the diagnostic mode is set to 827*0fca6ea1SDimitry Andric // strict (-fhlsl-strict-availability) because all relevant diagnostics 828*0fca6ea1SDimitry Andric // were already emitted in the DiagnoseUnguardedAvailability scan 829*0fca6ea1SDimitry Andric // (SemaAvailability.cpp). 830*0fca6ea1SDimitry Andric if (SemaRef.getLangOpts().HLSLStrictAvailability) 831*0fca6ea1SDimitry Andric return; 832*0fca6ea1SDimitry Andric 833*0fca6ea1SDimitry Andric // Do not report shader-stage-independent issues if scanning a function 834*0fca6ea1SDimitry Andric // that was already scanned in a different shader stage context (they would 835*0fca6ea1SDimitry Andric // be duplicate) 836*0fca6ea1SDimitry Andric if (ReportOnlyShaderStageIssues) 837*0fca6ea1SDimitry Andric return; 838*0fca6ea1SDimitry Andric 839*0fca6ea1SDimitry Andric } else { 840*0fca6ea1SDimitry Andric // The availability attribute has environment -> we need to know 841*0fca6ea1SDimitry Andric // the current stage context to property diagnose it. 842*0fca6ea1SDimitry Andric if (InUnknownShaderStageContext()) 843*0fca6ea1SDimitry Andric return; 844*0fca6ea1SDimitry Andric } 845*0fca6ea1SDimitry Andric 846*0fca6ea1SDimitry Andric // Check introduced version and if environment matches 847*0fca6ea1SDimitry Andric bool EnvironmentMatches = HasMatchingEnvironmentOrNone(AA); 848*0fca6ea1SDimitry Andric VersionTuple Introduced = AA->getIntroduced(); 849*0fca6ea1SDimitry Andric VersionTuple TargetVersion = 850*0fca6ea1SDimitry Andric SemaRef.Context.getTargetInfo().getPlatformMinVersion(); 851*0fca6ea1SDimitry Andric 852*0fca6ea1SDimitry Andric if (TargetVersion >= Introduced && EnvironmentMatches) 853*0fca6ea1SDimitry Andric return; 854*0fca6ea1SDimitry Andric 855*0fca6ea1SDimitry Andric // Emit diagnostic message 856*0fca6ea1SDimitry Andric const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo(); 857*0fca6ea1SDimitry Andric llvm::StringRef PlatformName( 858*0fca6ea1SDimitry Andric AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName())); 859*0fca6ea1SDimitry Andric 860*0fca6ea1SDimitry Andric llvm::StringRef CurrentEnvStr = 861*0fca6ea1SDimitry Andric llvm::Triple::getEnvironmentTypeName(GetCurrentShaderEnvironment()); 862*0fca6ea1SDimitry Andric 863*0fca6ea1SDimitry Andric llvm::StringRef AttrEnvStr = 864*0fca6ea1SDimitry Andric AA->getEnvironment() ? AA->getEnvironment()->getName() : ""; 865*0fca6ea1SDimitry Andric bool UseEnvironment = !AttrEnvStr.empty(); 866*0fca6ea1SDimitry Andric 867*0fca6ea1SDimitry Andric if (EnvironmentMatches) { 868*0fca6ea1SDimitry Andric SemaRef.Diag(Range.getBegin(), diag::warn_hlsl_availability) 869*0fca6ea1SDimitry Andric << Range << D << PlatformName << Introduced.getAsString() 870*0fca6ea1SDimitry Andric << UseEnvironment << CurrentEnvStr; 871*0fca6ea1SDimitry Andric } else { 872*0fca6ea1SDimitry Andric SemaRef.Diag(Range.getBegin(), diag::warn_hlsl_availability_unavailable) 873*0fca6ea1SDimitry Andric << Range << D; 874*0fca6ea1SDimitry Andric } 875*0fca6ea1SDimitry Andric 876*0fca6ea1SDimitry Andric SemaRef.Diag(D->getLocation(), diag::note_partial_availability_specified_here) 877*0fca6ea1SDimitry Andric << D << PlatformName << Introduced.getAsString() 878*0fca6ea1SDimitry Andric << SemaRef.Context.getTargetInfo().getPlatformMinVersion().getAsString() 879*0fca6ea1SDimitry Andric << UseEnvironment << AttrEnvStr << CurrentEnvStr; 880*0fca6ea1SDimitry Andric } 881*0fca6ea1SDimitry Andric 882*0fca6ea1SDimitry Andric } // namespace 883*0fca6ea1SDimitry Andric 884*0fca6ea1SDimitry Andric void SemaHLSL::DiagnoseAvailabilityViolations(TranslationUnitDecl *TU) { 885*0fca6ea1SDimitry Andric // Skip running the diagnostics scan if the diagnostic mode is 886*0fca6ea1SDimitry Andric // strict (-fhlsl-strict-availability) and the target shader stage is known 887*0fca6ea1SDimitry Andric // because all relevant diagnostics were already emitted in the 888*0fca6ea1SDimitry Andric // DiagnoseUnguardedAvailability scan (SemaAvailability.cpp). 889*0fca6ea1SDimitry Andric const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo(); 890*0fca6ea1SDimitry Andric if (SemaRef.getLangOpts().HLSLStrictAvailability && 891*0fca6ea1SDimitry Andric TI.getTriple().getEnvironment() != llvm::Triple::EnvironmentType::Library) 892*0fca6ea1SDimitry Andric return; 893*0fca6ea1SDimitry Andric 894*0fca6ea1SDimitry Andric DiagnoseHLSLAvailability(SemaRef).RunOnTranslationUnit(TU); 895*0fca6ea1SDimitry Andric } 896*0fca6ea1SDimitry Andric 897*0fca6ea1SDimitry Andric // Helper function for CheckHLSLBuiltinFunctionCall 898*0fca6ea1SDimitry Andric bool CheckVectorElementCallArgs(Sema *S, CallExpr *TheCall) { 899*0fca6ea1SDimitry Andric assert(TheCall->getNumArgs() > 1); 900*0fca6ea1SDimitry Andric ExprResult A = TheCall->getArg(0); 901*0fca6ea1SDimitry Andric 902*0fca6ea1SDimitry Andric QualType ArgTyA = A.get()->getType(); 903*0fca6ea1SDimitry Andric 904*0fca6ea1SDimitry Andric auto *VecTyA = ArgTyA->getAs<VectorType>(); 905*0fca6ea1SDimitry Andric SourceLocation BuiltinLoc = TheCall->getBeginLoc(); 906*0fca6ea1SDimitry Andric 907*0fca6ea1SDimitry Andric for (unsigned i = 1; i < TheCall->getNumArgs(); ++i) { 908*0fca6ea1SDimitry Andric ExprResult B = TheCall->getArg(i); 909*0fca6ea1SDimitry Andric QualType ArgTyB = B.get()->getType(); 910*0fca6ea1SDimitry Andric auto *VecTyB = ArgTyB->getAs<VectorType>(); 911*0fca6ea1SDimitry Andric if (VecTyA == nullptr && VecTyB == nullptr) 912*0fca6ea1SDimitry Andric return false; 913*0fca6ea1SDimitry Andric 914*0fca6ea1SDimitry Andric if (VecTyA && VecTyB) { 915*0fca6ea1SDimitry Andric bool retValue = false; 916*0fca6ea1SDimitry Andric if (VecTyA->getElementType() != VecTyB->getElementType()) { 917*0fca6ea1SDimitry Andric // Note: type promotion is intended to be handeled via the intrinsics 918*0fca6ea1SDimitry Andric // and not the builtin itself. 919*0fca6ea1SDimitry Andric S->Diag(TheCall->getBeginLoc(), 920*0fca6ea1SDimitry Andric diag::err_vec_builtin_incompatible_vector) 921*0fca6ea1SDimitry Andric << TheCall->getDirectCallee() << /*useAllTerminology*/ true 922*0fca6ea1SDimitry Andric << SourceRange(A.get()->getBeginLoc(), B.get()->getEndLoc()); 923*0fca6ea1SDimitry Andric retValue = true; 924*0fca6ea1SDimitry Andric } 925*0fca6ea1SDimitry Andric if (VecTyA->getNumElements() != VecTyB->getNumElements()) { 926*0fca6ea1SDimitry Andric // You should only be hitting this case if you are calling the builtin 927*0fca6ea1SDimitry Andric // directly. HLSL intrinsics should avoid this case via a 928*0fca6ea1SDimitry Andric // HLSLVectorTruncation. 929*0fca6ea1SDimitry Andric S->Diag(BuiltinLoc, diag::err_vec_builtin_incompatible_vector) 930*0fca6ea1SDimitry Andric << TheCall->getDirectCallee() << /*useAllTerminology*/ true 931*0fca6ea1SDimitry Andric << SourceRange(TheCall->getArg(0)->getBeginLoc(), 932*0fca6ea1SDimitry Andric TheCall->getArg(1)->getEndLoc()); 933*0fca6ea1SDimitry Andric retValue = true; 934*0fca6ea1SDimitry Andric } 935*0fca6ea1SDimitry Andric return retValue; 936*0fca6ea1SDimitry Andric } 937*0fca6ea1SDimitry Andric } 938*0fca6ea1SDimitry Andric 939*0fca6ea1SDimitry Andric // Note: if we get here one of the args is a scalar which 940*0fca6ea1SDimitry Andric // requires a VectorSplat on Arg0 or Arg1 941*0fca6ea1SDimitry Andric S->Diag(BuiltinLoc, diag::err_vec_builtin_non_vector) 942*0fca6ea1SDimitry Andric << TheCall->getDirectCallee() << /*useAllTerminology*/ true 943*0fca6ea1SDimitry Andric << SourceRange(TheCall->getArg(0)->getBeginLoc(), 944*0fca6ea1SDimitry Andric TheCall->getArg(1)->getEndLoc()); 945*0fca6ea1SDimitry Andric return true; 946*0fca6ea1SDimitry Andric } 947*0fca6ea1SDimitry Andric 948*0fca6ea1SDimitry Andric bool CheckArgsTypesAreCorrect( 949*0fca6ea1SDimitry Andric Sema *S, CallExpr *TheCall, QualType ExpectedType, 950*0fca6ea1SDimitry Andric llvm::function_ref<bool(clang::QualType PassedType)> Check) { 951*0fca6ea1SDimitry Andric for (unsigned i = 0; i < TheCall->getNumArgs(); ++i) { 952*0fca6ea1SDimitry Andric QualType PassedType = TheCall->getArg(i)->getType(); 953*0fca6ea1SDimitry Andric if (Check(PassedType)) { 954*0fca6ea1SDimitry Andric if (auto *VecTyA = PassedType->getAs<VectorType>()) 955*0fca6ea1SDimitry Andric ExpectedType = S->Context.getVectorType( 956*0fca6ea1SDimitry Andric ExpectedType, VecTyA->getNumElements(), VecTyA->getVectorKind()); 957*0fca6ea1SDimitry Andric S->Diag(TheCall->getArg(0)->getBeginLoc(), 958*0fca6ea1SDimitry Andric diag::err_typecheck_convert_incompatible) 959*0fca6ea1SDimitry Andric << PassedType << ExpectedType << 1 << 0 << 0; 960*0fca6ea1SDimitry Andric return true; 961*0fca6ea1SDimitry Andric } 962*0fca6ea1SDimitry Andric } 963*0fca6ea1SDimitry Andric return false; 964*0fca6ea1SDimitry Andric } 965*0fca6ea1SDimitry Andric 966*0fca6ea1SDimitry Andric bool CheckAllArgsHaveFloatRepresentation(Sema *S, CallExpr *TheCall) { 967*0fca6ea1SDimitry Andric auto checkAllFloatTypes = [](clang::QualType PassedType) -> bool { 968*0fca6ea1SDimitry Andric return !PassedType->hasFloatingRepresentation(); 969*0fca6ea1SDimitry Andric }; 970*0fca6ea1SDimitry Andric return CheckArgsTypesAreCorrect(S, TheCall, S->Context.FloatTy, 971*0fca6ea1SDimitry Andric checkAllFloatTypes); 972*0fca6ea1SDimitry Andric } 973*0fca6ea1SDimitry Andric 974*0fca6ea1SDimitry Andric bool CheckFloatOrHalfRepresentations(Sema *S, CallExpr *TheCall) { 975*0fca6ea1SDimitry Andric auto checkFloatorHalf = [](clang::QualType PassedType) -> bool { 976*0fca6ea1SDimitry Andric clang::QualType BaseType = 977*0fca6ea1SDimitry Andric PassedType->isVectorType() 978*0fca6ea1SDimitry Andric ? PassedType->getAs<clang::VectorType>()->getElementType() 979*0fca6ea1SDimitry Andric : PassedType; 980*0fca6ea1SDimitry Andric return !BaseType->isHalfType() && !BaseType->isFloat32Type(); 981*0fca6ea1SDimitry Andric }; 982*0fca6ea1SDimitry Andric return CheckArgsTypesAreCorrect(S, TheCall, S->Context.FloatTy, 983*0fca6ea1SDimitry Andric checkFloatorHalf); 984*0fca6ea1SDimitry Andric } 985*0fca6ea1SDimitry Andric 986*0fca6ea1SDimitry Andric bool CheckNoDoubleVectors(Sema *S, CallExpr *TheCall) { 987*0fca6ea1SDimitry Andric auto checkDoubleVector = [](clang::QualType PassedType) -> bool { 988*0fca6ea1SDimitry Andric if (const auto *VecTy = PassedType->getAs<VectorType>()) 989*0fca6ea1SDimitry Andric return VecTy->getElementType()->isDoubleType(); 990*0fca6ea1SDimitry Andric return false; 991*0fca6ea1SDimitry Andric }; 992*0fca6ea1SDimitry Andric return CheckArgsTypesAreCorrect(S, TheCall, S->Context.FloatTy, 993*0fca6ea1SDimitry Andric checkDoubleVector); 994*0fca6ea1SDimitry Andric } 995*0fca6ea1SDimitry Andric 996*0fca6ea1SDimitry Andric bool CheckUnsignedIntRepresentation(Sema *S, CallExpr *TheCall) { 997*0fca6ea1SDimitry Andric auto checkAllUnsignedTypes = [](clang::QualType PassedType) -> bool { 998*0fca6ea1SDimitry Andric return !PassedType->hasUnsignedIntegerRepresentation(); 999*0fca6ea1SDimitry Andric }; 1000*0fca6ea1SDimitry Andric return CheckArgsTypesAreCorrect(S, TheCall, S->Context.UnsignedIntTy, 1001*0fca6ea1SDimitry Andric checkAllUnsignedTypes); 1002*0fca6ea1SDimitry Andric } 1003*0fca6ea1SDimitry Andric 1004*0fca6ea1SDimitry Andric void SetElementTypeAsReturnType(Sema *S, CallExpr *TheCall, 1005*0fca6ea1SDimitry Andric QualType ReturnType) { 1006*0fca6ea1SDimitry Andric auto *VecTyA = TheCall->getArg(0)->getType()->getAs<VectorType>(); 1007*0fca6ea1SDimitry Andric if (VecTyA) 1008*0fca6ea1SDimitry Andric ReturnType = S->Context.getVectorType(ReturnType, VecTyA->getNumElements(), 1009*0fca6ea1SDimitry Andric VectorKind::Generic); 1010*0fca6ea1SDimitry Andric TheCall->setType(ReturnType); 1011*0fca6ea1SDimitry Andric } 1012*0fca6ea1SDimitry Andric 1013*0fca6ea1SDimitry Andric // Note: returning true in this case results in CheckBuiltinFunctionCall 1014*0fca6ea1SDimitry Andric // returning an ExprError 1015*0fca6ea1SDimitry Andric bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { 1016*0fca6ea1SDimitry Andric switch (BuiltinID) { 1017*0fca6ea1SDimitry Andric case Builtin::BI__builtin_hlsl_elementwise_all: 1018*0fca6ea1SDimitry Andric case Builtin::BI__builtin_hlsl_elementwise_any: { 1019*0fca6ea1SDimitry Andric if (SemaRef.checkArgCount(TheCall, 1)) 1020*0fca6ea1SDimitry Andric return true; 1021*0fca6ea1SDimitry Andric break; 1022*0fca6ea1SDimitry Andric } 1023*0fca6ea1SDimitry Andric case Builtin::BI__builtin_hlsl_elementwise_clamp: { 1024*0fca6ea1SDimitry Andric if (SemaRef.checkArgCount(TheCall, 3)) 1025*0fca6ea1SDimitry Andric return true; 1026*0fca6ea1SDimitry Andric if (CheckVectorElementCallArgs(&SemaRef, TheCall)) 1027*0fca6ea1SDimitry Andric return true; 1028*0fca6ea1SDimitry Andric if (SemaRef.BuiltinElementwiseTernaryMath( 1029*0fca6ea1SDimitry Andric TheCall, /*CheckForFloatArgs*/ 1030*0fca6ea1SDimitry Andric TheCall->getArg(0)->getType()->hasFloatingRepresentation())) 1031*0fca6ea1SDimitry Andric return true; 1032*0fca6ea1SDimitry Andric break; 1033*0fca6ea1SDimitry Andric } 1034*0fca6ea1SDimitry Andric case Builtin::BI__builtin_hlsl_dot: { 1035*0fca6ea1SDimitry Andric if (SemaRef.checkArgCount(TheCall, 2)) 1036*0fca6ea1SDimitry Andric return true; 1037*0fca6ea1SDimitry Andric if (CheckVectorElementCallArgs(&SemaRef, TheCall)) 1038*0fca6ea1SDimitry Andric return true; 1039*0fca6ea1SDimitry Andric if (SemaRef.BuiltinVectorToScalarMath(TheCall)) 1040*0fca6ea1SDimitry Andric return true; 1041*0fca6ea1SDimitry Andric if (CheckNoDoubleVectors(&SemaRef, TheCall)) 1042*0fca6ea1SDimitry Andric return true; 1043*0fca6ea1SDimitry Andric break; 1044*0fca6ea1SDimitry Andric } 1045*0fca6ea1SDimitry Andric case Builtin::BI__builtin_hlsl_elementwise_rcp: { 1046*0fca6ea1SDimitry Andric if (CheckAllArgsHaveFloatRepresentation(&SemaRef, TheCall)) 1047*0fca6ea1SDimitry Andric return true; 1048*0fca6ea1SDimitry Andric if (SemaRef.PrepareBuiltinElementwiseMathOneArgCall(TheCall)) 1049*0fca6ea1SDimitry Andric return true; 1050*0fca6ea1SDimitry Andric break; 1051*0fca6ea1SDimitry Andric } 1052*0fca6ea1SDimitry Andric case Builtin::BI__builtin_hlsl_elementwise_rsqrt: 1053*0fca6ea1SDimitry Andric case Builtin::BI__builtin_hlsl_elementwise_frac: { 1054*0fca6ea1SDimitry Andric if (CheckFloatOrHalfRepresentations(&SemaRef, TheCall)) 1055*0fca6ea1SDimitry Andric return true; 1056*0fca6ea1SDimitry Andric if (SemaRef.PrepareBuiltinElementwiseMathOneArgCall(TheCall)) 1057*0fca6ea1SDimitry Andric return true; 1058*0fca6ea1SDimitry Andric break; 1059*0fca6ea1SDimitry Andric } 1060*0fca6ea1SDimitry Andric case Builtin::BI__builtin_hlsl_elementwise_isinf: { 1061*0fca6ea1SDimitry Andric if (CheckFloatOrHalfRepresentations(&SemaRef, TheCall)) 1062*0fca6ea1SDimitry Andric return true; 1063*0fca6ea1SDimitry Andric if (SemaRef.PrepareBuiltinElementwiseMathOneArgCall(TheCall)) 1064*0fca6ea1SDimitry Andric return true; 1065*0fca6ea1SDimitry Andric SetElementTypeAsReturnType(&SemaRef, TheCall, getASTContext().BoolTy); 1066*0fca6ea1SDimitry Andric break; 1067*0fca6ea1SDimitry Andric } 1068*0fca6ea1SDimitry Andric case Builtin::BI__builtin_hlsl_lerp: { 1069*0fca6ea1SDimitry Andric if (SemaRef.checkArgCount(TheCall, 3)) 1070*0fca6ea1SDimitry Andric return true; 1071*0fca6ea1SDimitry Andric if (CheckVectorElementCallArgs(&SemaRef, TheCall)) 1072*0fca6ea1SDimitry Andric return true; 1073*0fca6ea1SDimitry Andric if (SemaRef.BuiltinElementwiseTernaryMath(TheCall)) 1074*0fca6ea1SDimitry Andric return true; 1075*0fca6ea1SDimitry Andric if (CheckFloatOrHalfRepresentations(&SemaRef, TheCall)) 1076*0fca6ea1SDimitry Andric return true; 1077*0fca6ea1SDimitry Andric break; 1078*0fca6ea1SDimitry Andric } 1079*0fca6ea1SDimitry Andric case Builtin::BI__builtin_hlsl_mad: { 1080*0fca6ea1SDimitry Andric if (SemaRef.checkArgCount(TheCall, 3)) 1081*0fca6ea1SDimitry Andric return true; 1082*0fca6ea1SDimitry Andric if (CheckVectorElementCallArgs(&SemaRef, TheCall)) 1083*0fca6ea1SDimitry Andric return true; 1084*0fca6ea1SDimitry Andric if (SemaRef.BuiltinElementwiseTernaryMath( 1085*0fca6ea1SDimitry Andric TheCall, /*CheckForFloatArgs*/ 1086*0fca6ea1SDimitry Andric TheCall->getArg(0)->getType()->hasFloatingRepresentation())) 1087*0fca6ea1SDimitry Andric return true; 1088*0fca6ea1SDimitry Andric break; 1089*0fca6ea1SDimitry Andric } 1090*0fca6ea1SDimitry Andric // Note these are llvm builtins that we want to catch invalid intrinsic 1091*0fca6ea1SDimitry Andric // generation. Normal handling of these builitns will occur elsewhere. 1092*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_bitreverse: { 1093*0fca6ea1SDimitry Andric if (CheckUnsignedIntRepresentation(&SemaRef, TheCall)) 1094*0fca6ea1SDimitry Andric return true; 1095*0fca6ea1SDimitry Andric break; 1096*0fca6ea1SDimitry Andric } 1097*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_acos: 1098*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_asin: 1099*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_atan: 1100*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_ceil: 1101*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_cos: 1102*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_cosh: 1103*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_exp: 1104*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_exp2: 1105*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_floor: 1106*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_log: 1107*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_log2: 1108*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_log10: 1109*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_pow: 1110*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_roundeven: 1111*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_sin: 1112*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_sinh: 1113*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_sqrt: 1114*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_tan: 1115*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_tanh: 1116*0fca6ea1SDimitry Andric case Builtin::BI__builtin_elementwise_trunc: { 1117*0fca6ea1SDimitry Andric if (CheckFloatOrHalfRepresentations(&SemaRef, TheCall)) 1118*0fca6ea1SDimitry Andric return true; 1119*0fca6ea1SDimitry Andric break; 1120*0fca6ea1SDimitry Andric } 1121*0fca6ea1SDimitry Andric } 1122*0fca6ea1SDimitry Andric return false; 1123bdd1243dSDimitry Andric } 1124