xref: /freebsd-src/contrib/llvm-project/clang/lib/Sema/SemaHLSL.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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