1//===- SemaHLSL.cpp - Semantic Analysis for HLSL constructs ---------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8// This implements Semantic Analysis for HLSL constructs.
9//===----------------------------------------------------------------------===//
10
11#include "clang/Sema/SemaHLSL.h"
12#include "clang/AST/ASTConsumer.h"
13#include "clang/AST/ASTContext.h"
14#include "clang/AST/Attr.h"
15#include "clang/AST/Decl.h"
16#include "clang/AST/DeclBase.h"
17#include "clang/AST/DeclCXX.h"
18#include "clang/AST/DeclarationName.h"
19#include "clang/AST/DynamicRecursiveASTVisitor.h"
20#include "clang/AST/Expr.h"
21#include "clang/AST/HLSLResource.h"
22#include "clang/AST/Type.h"
23#include "clang/AST/TypeBase.h"
24#include "clang/AST/TypeLoc.h"
25#include "clang/Basic/Builtins.h"
26#include "clang/Basic/DiagnosticSema.h"
27#include "clang/Basic/IdentifierTable.h"
28#include "clang/Basic/LLVM.h"
29#include "clang/Basic/SourceLocation.h"
30#include "clang/Basic/Specifiers.h"
31#include "clang/Basic/TargetInfo.h"
32#include "clang/Sema/Initialization.h"
33#include "clang/Sema/Lookup.h"
34#include "clang/Sema/ParsedAttr.h"
35#include "clang/Sema/Sema.h"
36#include "clang/Sema/Template.h"
37#include "llvm/ADT/ArrayRef.h"
38#include "llvm/ADT/STLExtras.h"
39#include "llvm/ADT/SmallVector.h"
40#include "llvm/ADT/StringExtras.h"
41#include "llvm/ADT/StringRef.h"
42#include "llvm/ADT/Twine.h"
43#include "llvm/Frontend/HLSL/HLSLBinding.h"
44#include "llvm/Frontend/HLSL/RootSignatureValidations.h"
45#include "llvm/Support/Casting.h"
46#include "llvm/Support/DXILABI.h"
47#include "llvm/Support/ErrorHandling.h"
48#include "llvm/Support/FormatVariadic.h"
49#include "llvm/TargetParser/Triple.h"
50#include <cmath>
51#include <cstddef>
52#include <iterator>
53#include <utility>
54
55using namespace clang;
56using namespace clang::hlsl;
57using llvm::hlsl::IOType;
58using llvm::hlsl::SemanticStageInfo;
59using SemanticKind = llvm::dxbc::PSV::SemanticKind;
60using RegisterType = HLSLResourceBindingAttr::RegisterType;
61
62static CXXRecordDecl *createHostLayoutStruct(Sema &S,
63 CXXRecordDecl *StructDecl);
64
65static RegisterType getRegisterType(ResourceClass RC) {
66 switch (RC) {
67 case ResourceClass::SRV:
68 return RegisterType::SRV;
69 case ResourceClass::UAV:
70 return RegisterType::UAV;
71 case ResourceClass::CBuffer:
72 return RegisterType::CBuffer;
73 case ResourceClass::Sampler:
74 return RegisterType::Sampler;
75 }
76 llvm_unreachable("unexpected ResourceClass value");
77}
78
79static RegisterType getRegisterType(const HLSLAttributedResourceType *ResTy) {
80 return getRegisterType(RC: ResTy->getAttrs().ResourceClass);
81}
82
83static LangAS getLangASFromResourceClass(ResourceClass RC) {
84 switch (RC) {
85 case ResourceClass::SRV:
86 case ResourceClass::UAV:
87 return LangAS::hlsl_device;
88 case ResourceClass::CBuffer:
89 return LangAS::hlsl_constant;
90 case ResourceClass::Sampler:
91 return LangAS::hlsl_device;
92 }
93 llvm_unreachable("unexpected ResourceClass value");
94}
95
96// Converts the first letter of string Slot to RegisterType.
97// Returns false if the letter does not correspond to a valid register type.
98static bool convertToRegisterType(StringRef Slot, RegisterType *RT) {
99 assert(RT != nullptr);
100 switch (Slot[0]) {
101 case 't':
102 case 'T':
103 *RT = RegisterType::SRV;
104 return true;
105 case 'u':
106 case 'U':
107 *RT = RegisterType::UAV;
108 return true;
109 case 'b':
110 case 'B':
111 *RT = RegisterType::CBuffer;
112 return true;
113 case 's':
114 case 'S':
115 *RT = RegisterType::Sampler;
116 return true;
117 case 'c':
118 case 'C':
119 *RT = RegisterType::C;
120 return true;
121 case 'i':
122 case 'I':
123 *RT = RegisterType::I;
124 return true;
125 default:
126 return false;
127 }
128}
129
130static char getRegisterTypeChar(RegisterType RT) {
131 switch (RT) {
132 case RegisterType::SRV:
133 return 't';
134 case RegisterType::UAV:
135 return 'u';
136 case RegisterType::CBuffer:
137 return 'b';
138 case RegisterType::Sampler:
139 return 's';
140 case RegisterType::C:
141 return 'c';
142 case RegisterType::I:
143 return 'i';
144 }
145 llvm_unreachable("unexpected RegisterType value");
146}
147
148static ResourceClass getResourceClass(RegisterType RT) {
149 switch (RT) {
150 case RegisterType::SRV:
151 return ResourceClass::SRV;
152 case RegisterType::UAV:
153 return ResourceClass::UAV;
154 case RegisterType::CBuffer:
155 return ResourceClass::CBuffer;
156 case RegisterType::Sampler:
157 return ResourceClass::Sampler;
158 case RegisterType::C:
159 case RegisterType::I:
160 // Deliberately falling through to the unreachable below.
161 break;
162 }
163 llvm_unreachable("unexpected RegisterType value");
164}
165
166static Builtin::ID getSpecConstBuiltinId(const Type *Type) {
167 const auto *BT = dyn_cast<BuiltinType>(Val: Type);
168 if (!BT) {
169 if (!Type->isEnumeralType())
170 return Builtin::NotBuiltin;
171 return Builtin::BI__builtin_get_spirv_spec_constant_int;
172 }
173
174 switch (BT->getKind()) {
175 case BuiltinType::Bool:
176 return Builtin::BI__builtin_get_spirv_spec_constant_bool;
177 case BuiltinType::Short:
178 return Builtin::BI__builtin_get_spirv_spec_constant_short;
179 case BuiltinType::Int:
180 return Builtin::BI__builtin_get_spirv_spec_constant_int;
181 case BuiltinType::LongLong:
182 return Builtin::BI__builtin_get_spirv_spec_constant_longlong;
183 case BuiltinType::UShort:
184 return Builtin::BI__builtin_get_spirv_spec_constant_ushort;
185 case BuiltinType::UInt:
186 return Builtin::BI__builtin_get_spirv_spec_constant_uint;
187 case BuiltinType::ULongLong:
188 return Builtin::BI__builtin_get_spirv_spec_constant_ulonglong;
189 case BuiltinType::Half:
190 return Builtin::BI__builtin_get_spirv_spec_constant_half;
191 case BuiltinType::Float:
192 return Builtin::BI__builtin_get_spirv_spec_constant_float;
193 case BuiltinType::Double:
194 return Builtin::BI__builtin_get_spirv_spec_constant_double;
195 default:
196 return Builtin::NotBuiltin;
197 }
198}
199
200static StringRef createRegisterString(ASTContext &AST, RegisterType RegType,
201 unsigned N) {
202 llvm::SmallString<16> Buffer;
203 llvm::raw_svector_ostream OS(Buffer);
204 OS << getRegisterTypeChar(RT: RegType);
205 OS << N;
206 return AST.backupStr(S: OS.str());
207}
208
209DeclBindingInfo *ResourceBindings::addDeclBindingInfo(const VarDecl *VD,
210 ResourceClass ResClass) {
211 assert(getDeclBindingInfo(VD, ResClass) == nullptr &&
212 "DeclBindingInfo already added");
213 assert(!hasBindingInfoForDecl(VD) || BindingsList.back().Decl == VD);
214 // VarDecl may have multiple entries for different resource classes.
215 // DeclToBindingListIndex stores the index of the first binding we saw
216 // for this decl. If there are any additional ones then that index
217 // shouldn't be updated.
218 DeclToBindingListIndex.try_emplace(Key: VD, Args: BindingsList.size());
219 return &BindingsList.emplace_back(Args&: VD, Args&: ResClass);
220}
221
222DeclBindingInfo *ResourceBindings::getDeclBindingInfo(const VarDecl *VD,
223 ResourceClass ResClass) {
224 auto Entry = DeclToBindingListIndex.find(Val: VD);
225 if (Entry != DeclToBindingListIndex.end()) {
226 for (unsigned Index = Entry->getSecond();
227 Index < BindingsList.size() && BindingsList[Index].Decl == VD;
228 ++Index) {
229 if (BindingsList[Index].ResClass == ResClass)
230 return &BindingsList[Index];
231 }
232 }
233 return nullptr;
234}
235
236bool ResourceBindings::hasBindingInfoForDecl(const VarDecl *VD) const {
237 return DeclToBindingListIndex.contains(Val: VD);
238}
239
240SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S) {}
241
242Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
243 SourceLocation KwLoc, IdentifierInfo *Ident,
244 SourceLocation IdentLoc,
245 SourceLocation LBrace) {
246 // For anonymous namespace, take the location of the left brace.
247 DeclContext *LexicalParent = SemaRef.getCurLexicalContext();
248 HLSLBufferDecl *Result = HLSLBufferDecl::Create(
249 C&: getASTContext(), LexicalParent, CBuffer, KwLoc, ID: Ident, IDLoc: IdentLoc, LBrace);
250
251 // if CBuffer is false, then it's a TBuffer
252 auto RC = CBuffer ? llvm::hlsl::ResourceClass::CBuffer
253 : llvm::hlsl::ResourceClass::SRV;
254 Result->addAttr(A: HLSLResourceClassAttr::CreateImplicit(Ctx&: getASTContext(), ResourceClass: RC));
255
256 SemaRef.PushOnScopeChains(D: Result, S: BufferScope);
257 SemaRef.PushDeclContext(S: BufferScope, DC: Result);
258
259 return Result;
260}
261
262static unsigned calculateLegacyCbufferFieldAlign(const ASTContext &Context,
263 QualType T) {
264 // Arrays, Matrices, and Structs are always aligned to new buffer rows
265 if (T->isArrayType() || T->isStructureType() || T->isConstantMatrixType())
266 return 16;
267
268 // Vectors are aligned to the type they contain
269 if (const VectorType *VT = T->getAs<VectorType>())
270 return calculateLegacyCbufferFieldAlign(Context, T: VT->getElementType());
271
272 assert(Context.getTypeSize(T) <= 64 &&
273 "Scalar bit widths larger than 64 not supported");
274
275 // Scalar types are aligned to their byte width
276 return Context.getTypeSize(T) / 8;
277}
278
279// Calculate the size of a legacy cbuffer type in bytes based on
280// https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules
281static unsigned calculateLegacyCbufferSize(const ASTContext &Context,
282 QualType T) {
283 constexpr unsigned CBufferAlign = 16;
284 if (const auto *RD = T->getAsRecordDecl()) {
285 unsigned Size = 0;
286 for (const FieldDecl *Field : RD->fields()) {
287 QualType Ty = Field->getType();
288 unsigned FieldSize = calculateLegacyCbufferSize(Context, T: Ty);
289 unsigned FieldAlign = calculateLegacyCbufferFieldAlign(Context, T: Ty);
290
291 // If the field crosses the row boundary after alignment it drops to the
292 // next row
293 unsigned AlignSize = llvm::alignTo(Value: Size, Align: FieldAlign);
294 if ((AlignSize % CBufferAlign) + FieldSize > CBufferAlign) {
295 FieldAlign = CBufferAlign;
296 }
297
298 Size = llvm::alignTo(Value: Size, Align: FieldAlign);
299 Size += FieldSize;
300 }
301 return Size;
302 }
303
304 if (const ConstantArrayType *AT = Context.getAsConstantArrayType(T)) {
305 unsigned ElementCount = AT->getSize().getZExtValue();
306 if (ElementCount == 0)
307 return 0;
308
309 unsigned ElementSize =
310 calculateLegacyCbufferSize(Context, T: AT->getElementType());
311 unsigned AlignedElementSize = llvm::alignTo(Value: ElementSize, Align: CBufferAlign);
312 return AlignedElementSize * (ElementCount - 1) + ElementSize;
313 }
314
315 if (const VectorType *VT = T->getAs<VectorType>()) {
316 unsigned ElementCount = VT->getNumElements();
317 unsigned ElementSize =
318 calculateLegacyCbufferSize(Context, T: VT->getElementType());
319 return ElementSize * ElementCount;
320 }
321
322 return Context.getTypeSize(T) / 8;
323}
324
325// Validate packoffset:
326// - if packoffset it used it must be set on all declarations inside the buffer
327// - packoffset ranges must not overlap
328static void validatePackoffset(Sema &S, HLSLBufferDecl *BufDecl) {
329 llvm::SmallVector<std::pair<VarDecl *, HLSLPackOffsetAttr *>> PackOffsetVec;
330
331 // Make sure the packoffset annotations are either on all declarations
332 // or on none.
333 bool HasPackOffset = false;
334 bool HasNonPackOffset = false;
335 for (auto *Field : BufDecl->buffer_decls()) {
336 VarDecl *Var = dyn_cast<VarDecl>(Val: Field);
337 if (!Var)
338 continue;
339 if (Field->hasAttr<HLSLPackOffsetAttr>()) {
340 PackOffsetVec.emplace_back(Args&: Var, Args: Field->getAttr<HLSLPackOffsetAttr>());
341 HasPackOffset = true;
342 } else {
343 HasNonPackOffset = true;
344 }
345 }
346
347 if (!HasPackOffset)
348 return;
349
350 if (HasNonPackOffset)
351 S.Diag(Loc: BufDecl->getLocation(), DiagID: diag::warn_hlsl_packoffset_mix);
352
353 // Make sure there is no overlap in packoffset - sort PackOffsetVec by offset
354 // and compare adjacent values.
355 bool IsValid = true;
356 ASTContext &Context = S.getASTContext();
357 std::sort(first: PackOffsetVec.begin(), last: PackOffsetVec.end(),
358 comp: [](const std::pair<VarDecl *, HLSLPackOffsetAttr *> &LHS,
359 const std::pair<VarDecl *, HLSLPackOffsetAttr *> &RHS) {
360 return LHS.second->getOffsetInBytes() <
361 RHS.second->getOffsetInBytes();
362 });
363 for (unsigned i = 0; i < PackOffsetVec.size() - 1; i++) {
364 VarDecl *Var = PackOffsetVec[i].first;
365 HLSLPackOffsetAttr *Attr = PackOffsetVec[i].second;
366 unsigned Size = calculateLegacyCbufferSize(Context, T: Var->getType());
367 unsigned Begin = Attr->getOffsetInBytes();
368 unsigned End = Begin + Size;
369 unsigned NextBegin = PackOffsetVec[i + 1].second->getOffsetInBytes();
370 if (End > NextBegin) {
371 VarDecl *NextVar = PackOffsetVec[i + 1].first;
372 S.Diag(Loc: NextVar->getLocation(), DiagID: diag::err_hlsl_packoffset_overlap)
373 << NextVar << Var;
374 IsValid = false;
375 }
376 }
377 BufDecl->setHasValidPackoffset(IsValid);
378}
379
380// Returns true if the array has a zero size = if any of the dimensions is 0
381static bool isZeroSizedArray(const ConstantArrayType *CAT) {
382 while (CAT && !CAT->isZeroSize())
383 CAT = dyn_cast<ConstantArrayType>(
384 Val: CAT->getElementType()->getUnqualifiedDesugaredType());
385 return CAT != nullptr;
386}
387
388static bool isResourceRecordTypeOrArrayOf(QualType Ty) {
389 return Ty->isHLSLResourceRecord() || Ty->isHLSLResourceRecordArray();
390}
391
392static bool isResourceRecordTypeOrArrayOf(VarDecl *VD) {
393 return isResourceRecordTypeOrArrayOf(Ty: VD->getType());
394}
395
396static const HLSLAttributedResourceType *
397getResourceArrayHandleType(QualType QT) {
398 assert(QT->isHLSLResourceRecordArray() &&
399 "expected array of resource records");
400 const Type *Ty = QT->getUnqualifiedDesugaredType();
401 while (const ArrayType *AT = dyn_cast<ArrayType>(Val: Ty))
402 Ty = AT->getArrayElementTypeNoTypeQual()->getUnqualifiedDesugaredType();
403 return HLSLAttributedResourceType::findHandleTypeOnResource(RT: Ty);
404}
405
406static const HLSLAttributedResourceType *
407getResourceArrayHandleType(VarDecl *VD) {
408 return getResourceArrayHandleType(QT: VD->getType());
409}
410
411// Returns true if the type is a leaf element type that is not valid to be
412// included in HLSL Buffer, such as a resource class, empty struct, zero-sized
413// array, or a builtin intangible type. Returns false it is a valid leaf element
414// type or if it is a record type that needs to be inspected further.
415static bool isInvalidConstantBufferLeafElementType(const Type *Ty) {
416 Ty = Ty->getUnqualifiedDesugaredType();
417 if (Ty->isHLSLResourceRecord() || Ty->isHLSLResourceRecordArray())
418 return true;
419 if (const auto *RD = Ty->getAsCXXRecordDecl())
420 return RD->isEmpty();
421 if (Ty->isConstantArrayType() &&
422 isZeroSizedArray(CAT: cast<ConstantArrayType>(Val: Ty)))
423 return true;
424 if (Ty->isHLSLBuiltinIntangibleType() || Ty->isHLSLAttributedResourceType())
425 return true;
426 return false;
427}
428
429// Returns true if the struct contains at least one element that prevents it
430// from being included inside HLSL Buffer as is, such as an intangible type,
431// empty struct, or zero-sized array. If it does, a new implicit layout struct
432// needs to be created for HLSL Buffer use that will exclude these unwanted
433// declarations (see createHostLayoutStruct function).
434static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
435 if (RD->isHLSLIntangible() || RD->isEmpty())
436 return true;
437 // check fields
438 for (const FieldDecl *Field : RD->fields()) {
439 QualType Ty = Field->getType();
440 if (isInvalidConstantBufferLeafElementType(Ty: Ty.getTypePtr()))
441 return true;
442 if (const auto *RD = Ty->getAsCXXRecordDecl();
443 RD && requiresImplicitBufferLayoutStructure(RD))
444 return true;
445 }
446 // check bases
447 for (const CXXBaseSpecifier &Base : RD->bases())
448 if (requiresImplicitBufferLayoutStructure(
449 RD: Base.getType()->castAsCXXRecordDecl()))
450 return true;
451 return false;
452}
453
454static CXXRecordDecl *findRecordDeclInContext(IdentifierInfo *II,
455 DeclContext *DC) {
456 CXXRecordDecl *RD = nullptr;
457 for (NamedDecl *Decl :
458 DC->getNonTransparentContext()->lookup(Name: DeclarationName(II))) {
459 if (CXXRecordDecl *FoundRD = dyn_cast<CXXRecordDecl>(Val: Decl)) {
460 assert(RD == nullptr &&
461 "there should be at most 1 record by a given name in a scope");
462 RD = FoundRD;
463 }
464 }
465 return RD;
466}
467
468// Creates a name for buffer layout struct using the provide name base.
469// If the name must be unique (not previously defined), a suffix is added
470// until a unique name is found.
471static IdentifierInfo *getHostLayoutStructName(Sema &S, NamedDecl *BaseDecl,
472 bool MustBeUnique) {
473 ASTContext &AST = S.getASTContext();
474
475 IdentifierInfo *NameBaseII = BaseDecl->getIdentifier();
476 llvm::SmallString<64> Name("__cblayout_");
477 if (NameBaseII) {
478 Name.append(RHS: NameBaseII->getName());
479 } else {
480 // anonymous struct
481 Name.append(RHS: "anon");
482 MustBeUnique = true;
483 }
484
485 size_t NameLength = Name.size();
486 IdentifierInfo *II = &AST.Idents.get(Name, TokenCode: tok::TokenKind::identifier);
487 if (!MustBeUnique)
488 return II;
489
490 unsigned suffix = 0;
491 while (true) {
492 if (suffix != 0) {
493 Name.append(RHS: "_");
494 Name.append(RHS: llvm::Twine(suffix).str());
495 II = &AST.Idents.get(Name, TokenCode: tok::TokenKind::identifier);
496 }
497 if (!findRecordDeclInContext(II, DC: BaseDecl->getDeclContext()))
498 return II;
499 // declaration with that name already exists - increment suffix and try
500 // again until unique name is found
501 suffix++;
502 Name.truncate(N: NameLength);
503 };
504}
505
506static const Type *createHostLayoutType(Sema &S, const Type *Ty) {
507 ASTContext &AST = S.getASTContext();
508 if (auto *RD = Ty->getAsCXXRecordDecl()) {
509 if (!requiresImplicitBufferLayoutStructure(RD))
510 return Ty;
511 RD = createHostLayoutStruct(S, StructDecl: RD);
512 if (!RD)
513 return nullptr;
514 return AST.getCanonicalTagType(TD: RD)->getTypePtr();
515 }
516
517 if (const auto *CAT = dyn_cast<ConstantArrayType>(Val: Ty)) {
518 const Type *ElementTy = createHostLayoutType(
519 S, Ty: CAT->getElementType()->getUnqualifiedDesugaredType());
520 if (!ElementTy)
521 return nullptr;
522 return AST
523 .getConstantArrayType(EltTy: QualType(ElementTy, 0), ArySize: CAT->getSize(), SizeExpr: nullptr,
524 ASM: CAT->getSizeModifier(),
525 IndexTypeQuals: CAT->getIndexTypeCVRQualifiers())
526 .getTypePtr();
527 }
528 return Ty;
529}
530
531// Returns the type to use for a host layout struct field. For most types this
532// is the unqualified desugared type. Matrix types, however, retain their sugar
533// so that the row_major/column_major orientation (carried as an AttributedType)
534// is preserved; the orientation determines the in-memory cbuffer layout.
535static const Type *getHostLayoutFieldType(QualType QT) {
536 const Type *Desugared = QT->getUnqualifiedDesugaredType();
537 if (Desugared->isConstantMatrixType())
538 return QT.getTypePtr();
539 return Desugared;
540}
541
542// Creates a field declaration of given name and type for HLSL buffer layout
543// struct. Returns nullptr if the type cannot be use in HLSL Buffer layout.
544static FieldDecl *createFieldForHostLayoutStruct(Sema &S, const Type *Ty,
545 IdentifierInfo *II,
546 CXXRecordDecl *LayoutStruct) {
547 if (isInvalidConstantBufferLeafElementType(Ty))
548 return nullptr;
549
550 Ty = createHostLayoutType(S, Ty);
551 if (!Ty)
552 return nullptr;
553
554 QualType QT = QualType(Ty, 0);
555 ASTContext &AST = S.getASTContext();
556 TypeSourceInfo *TSI = AST.getTrivialTypeSourceInfo(T: QT, Loc: SourceLocation());
557 auto *Field = FieldDecl::Create(C: AST, DC: LayoutStruct, StartLoc: SourceLocation(),
558 IdLoc: SourceLocation(), Id: II, T: QT, TInfo: TSI, BW: nullptr, Mutable: false,
559 InitStyle: InClassInitStyle::ICIS_NoInit);
560 Field->setAccess(AccessSpecifier::AS_public);
561 return Field;
562}
563
564// Creates host layout struct for a struct included in HLSL Buffer.
565// The layout struct will include only fields that are allowed in HLSL buffer.
566// These fields will be filtered out:
567// - resource classes
568// - empty structs
569// - zero-sized arrays
570// Returns nullptr if the resulting layout struct would be empty.
571static CXXRecordDecl *createHostLayoutStruct(Sema &S,
572 CXXRecordDecl *StructDecl) {
573 assert(requiresImplicitBufferLayoutStructure(StructDecl) &&
574 "struct is already HLSL buffer compatible");
575
576 ASTContext &AST = S.getASTContext();
577 DeclContext *DC = StructDecl->getDeclContext();
578 IdentifierInfo *II = getHostLayoutStructName(S, BaseDecl: StructDecl, MustBeUnique: false);
579
580 // reuse existing if the layout struct if it already exists
581 if (CXXRecordDecl *RD = findRecordDeclInContext(II, DC))
582 return RD;
583
584 CXXRecordDecl *LS =
585 CXXRecordDecl::Create(C: AST, TK: TagDecl::TagKind::Struct, DC, StartLoc: SourceLocation(),
586 IdLoc: SourceLocation(), Id: II);
587 LS->setImplicit(true);
588 LS->addAttr(A: PackedAttr::CreateImplicit(Ctx&: AST));
589 LS->startDefinition();
590
591 // copy base struct, create HLSL Buffer compatible version if needed
592 if (unsigned NumBases = StructDecl->getNumBases()) {
593 assert(NumBases == 1 && "HLSL supports only one base type");
594 (void)NumBases;
595 CXXBaseSpecifier Base = *StructDecl->bases_begin();
596 CXXRecordDecl *BaseDecl = Base.getType()->castAsCXXRecordDecl();
597 if (requiresImplicitBufferLayoutStructure(RD: BaseDecl)) {
598 BaseDecl = createHostLayoutStruct(S, StructDecl: BaseDecl);
599 if (BaseDecl) {
600 TypeSourceInfo *TSI =
601 AST.getTrivialTypeSourceInfo(T: AST.getCanonicalTagType(TD: BaseDecl));
602 Base = CXXBaseSpecifier(SourceRange(), false, StructDecl->isClass(),
603 AS_none, TSI, SourceLocation());
604 }
605 }
606 if (BaseDecl) {
607 const CXXBaseSpecifier *BasesArray[1] = {&Base};
608 LS->setBases(Bases: BasesArray, NumBases: 1);
609 }
610 }
611
612 // filter struct fields
613 for (const FieldDecl *FD : StructDecl->fields()) {
614 const Type *Ty = getHostLayoutFieldType(QT: FD->getType());
615 if (FieldDecl *NewFD =
616 createFieldForHostLayoutStruct(S, Ty, II: FD->getIdentifier(), LayoutStruct: LS))
617 LS->addDecl(D: NewFD);
618 }
619 LS->completeDefinition();
620
621 if (LS->field_empty() && LS->getNumBases() == 0)
622 return nullptr;
623
624 DC->addDecl(D: LS);
625 return LS;
626}
627
628// Creates host layout struct for HLSL Buffer. The struct will include only
629// fields of types that are allowed in HLSL buffer and it will filter out:
630// - static or groupshared variable declarations
631// - resource classes
632// - empty structs
633// - zero-sized arrays
634// - non-variable declarations
635// The layout struct will be added to the HLSLBufferDecl declarations.
636static void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) {
637 ASTContext &AST = S.getASTContext();
638 IdentifierInfo *II = getHostLayoutStructName(S, BaseDecl: BufDecl, MustBeUnique: true);
639
640 CXXRecordDecl *LS =
641 CXXRecordDecl::Create(C: AST, TK: TagDecl::TagKind::Struct, DC: BufDecl,
642 StartLoc: SourceLocation(), IdLoc: SourceLocation(), Id: II);
643 LS->addAttr(A: PackedAttr::CreateImplicit(Ctx&: AST));
644 LS->setImplicit(true);
645 LS->startDefinition();
646
647 for (Decl *D : BufDecl->buffer_decls()) {
648 VarDecl *VD = dyn_cast<VarDecl>(Val: D);
649 if (!VD || VD->getStorageClass() == SC_Static ||
650 VD->getType().getAddressSpace() == LangAS::hlsl_groupshared)
651 continue;
652 const Type *Ty = getHostLayoutFieldType(QT: VD->getType());
653
654 FieldDecl *FD =
655 createFieldForHostLayoutStruct(S, Ty, II: VD->getIdentifier(), LayoutStruct: LS);
656 // Declarations collected for the default $Globals constant buffer have
657 // already been checked to have non-empty cbuffer layout, so
658 // createFieldForHostLayoutStruct should always succeed. These declarations
659 // already have their address space set to hlsl_constant.
660 // For declarations in a named cbuffer block
661 // createFieldForHostLayoutStruct can still return nullptr if the type
662 // is empty (does not have a cbuffer layout).
663 assert((FD || VD->getType().getAddressSpace() != LangAS::hlsl_constant) &&
664 "host layout field for $Globals decl failed to be created");
665 if (FD) {
666 // Add the field decl to the layout struct.
667 LS->addDecl(D: FD);
668 if (VD->getType().getAddressSpace() != LangAS::hlsl_constant) {
669 // Update address space of the original decl to hlsl_constant.
670 QualType NewTy =
671 AST.getAddrSpaceQualType(T: VD->getType(), AddressSpace: LangAS::hlsl_constant);
672 VD->setType(NewTy);
673 }
674 }
675 }
676 LS->completeDefinition();
677 BufDecl->addLayoutStruct(LS);
678}
679
680static void addImplicitBindingAttrToDecl(Sema &S, Decl *D, RegisterType RT,
681 uint32_t ImplicitBindingOrderID) {
682 auto *Attr =
683 HLSLResourceBindingAttr::CreateImplicit(Ctx&: S.getASTContext(), Slot: "", Space: "0", Range: {});
684 Attr->setBinding(RT, SlotNum: std::nullopt, SpaceNum: 0);
685 Attr->setImplicitBindingOrderID(ImplicitBindingOrderID);
686 D->addAttr(A: Attr);
687}
688
689// Handle end of cbuffer/tbuffer declaration
690void SemaHLSL::ActOnFinishBuffer(Decl *Dcl, SourceLocation RBrace) {
691 auto *BufDecl = cast<HLSLBufferDecl>(Val: Dcl);
692 BufDecl->setRBraceLoc(RBrace);
693
694 validatePackoffset(S&: SemaRef, BufDecl);
695
696 createHostLayoutStructForBuffer(S&: SemaRef, BufDecl);
697
698 // Handle implicit binding if needed.
699 ResourceBindingAttrs ResourceAttrs(Dcl);
700 if (!ResourceAttrs.isExplicit()) {
701 SemaRef.Diag(Loc: Dcl->getLocation(), DiagID: diag::warn_hlsl_implicit_binding);
702 // Use HLSLResourceBindingAttr to transfer implicit binding order_ID
703 // to codegen. If it does not exist, create an implicit attribute.
704 uint32_t OrderID = getNextImplicitBindingOrderID();
705 if (ResourceAttrs.hasBinding())
706 ResourceAttrs.setImplicitOrderID(OrderID);
707 else
708 addImplicitBindingAttrToDecl(S&: SemaRef, D: BufDecl,
709 RT: BufDecl->isCBuffer() ? RegisterType::CBuffer
710 : RegisterType::SRV,
711 ImplicitBindingOrderID: OrderID);
712 }
713
714 SemaRef.PopDeclContext();
715}
716
717HLSLNumThreadsAttr *SemaHLSL::mergeNumThreadsAttr(Decl *D,
718 const AttributeCommonInfo &AL,
719 int X, int Y, int Z) {
720 if (HLSLNumThreadsAttr *NT = D->getAttr<HLSLNumThreadsAttr>()) {
721 if (NT->getX() != X || NT->getY() != Y || NT->getZ() != Z) {
722 Diag(Loc: NT->getLocation(), DiagID: diag::err_hlsl_attribute_param_mismatch) << AL;
723 Diag(Loc: AL.getLoc(), DiagID: diag::note_conflicting_attribute);
724 }
725 return nullptr;
726 }
727 return ::new (getASTContext())
728 HLSLNumThreadsAttr(getASTContext(), AL, X, Y, Z);
729}
730
731HLSLWaveSizeAttr *SemaHLSL::mergeWaveSizeAttr(Decl *D,
732 const AttributeCommonInfo &AL,
733 int Min, int Max, int Preferred,
734 int SpelledArgsCount) {
735 if (HLSLWaveSizeAttr *WS = D->getAttr<HLSLWaveSizeAttr>()) {
736 if (WS->getMin() != Min || WS->getMax() != Max ||
737 WS->getPreferred() != Preferred ||
738 WS->getSpelledArgsCount() != SpelledArgsCount) {
739 Diag(Loc: WS->getLocation(), DiagID: diag::err_hlsl_attribute_param_mismatch) << AL;
740 Diag(Loc: AL.getLoc(), DiagID: diag::note_conflicting_attribute);
741 }
742 return nullptr;
743 }
744 HLSLWaveSizeAttr *Result = ::new (getASTContext())
745 HLSLWaveSizeAttr(getASTContext(), AL, Min, Max, Preferred);
746 Result->setSpelledArgsCount(SpelledArgsCount);
747 return Result;
748}
749
750HLSLVkConstantIdAttr *
751SemaHLSL::mergeVkConstantIdAttr(Decl *D, const AttributeCommonInfo &AL,
752 int Id) {
753
754 auto &TargetInfo = getASTContext().getTargetInfo();
755 if (TargetInfo.getTriple().getArch() != llvm::Triple::spirv) {
756 Diag(Loc: AL.getLoc(), DiagID: diag::warn_attribute_ignored) << AL;
757 return nullptr;
758 }
759
760 auto *VD = cast<VarDecl>(Val: D);
761
762 if (getSpecConstBuiltinId(Type: VD->getType()->getUnqualifiedDesugaredType()) ==
763 Builtin::NotBuiltin) {
764 Diag(Loc: VD->getLocation(), DiagID: diag::err_specialization_const);
765 return nullptr;
766 }
767
768 if (!VD->getType().isConstQualified()) {
769 Diag(Loc: VD->getLocation(), DiagID: diag::err_specialization_const);
770 return nullptr;
771 }
772
773 if (HLSLVkConstantIdAttr *CI = D->getAttr<HLSLVkConstantIdAttr>()) {
774 if (CI->getId() != Id) {
775 Diag(Loc: CI->getLocation(), DiagID: diag::err_hlsl_attribute_param_mismatch) << AL;
776 Diag(Loc: AL.getLoc(), DiagID: diag::note_conflicting_attribute);
777 }
778 return nullptr;
779 }
780
781 HLSLVkConstantIdAttr *Result =
782 ::new (getASTContext()) HLSLVkConstantIdAttr(getASTContext(), AL, Id);
783 return Result;
784}
785
786HLSLShaderAttr *
787SemaHLSL::mergeShaderAttr(Decl *D, const AttributeCommonInfo &AL,
788 llvm::Triple::EnvironmentType ShaderType) {
789 if (HLSLShaderAttr *NT = D->getAttr<HLSLShaderAttr>()) {
790 if (NT->getType() != ShaderType) {
791 Diag(Loc: NT->getLocation(), DiagID: diag::err_hlsl_attribute_param_mismatch) << AL;
792 Diag(Loc: AL.getLoc(), DiagID: diag::note_conflicting_attribute);
793 }
794 return nullptr;
795 }
796 return HLSLShaderAttr::Create(Ctx&: getASTContext(), Type: ShaderType, CommonInfo: AL);
797}
798
799HLSLParamModifierAttr *
800SemaHLSL::mergeParamModifierAttr(Decl *D, const AttributeCommonInfo &AL,
801 HLSLParamModifierAttr::Spelling Spelling) {
802 // We can only merge an `in` attribute with an `out` attribute. All other
803 // combinations of duplicated attributes are ill-formed.
804 if (HLSLParamModifierAttr *PA = D->getAttr<HLSLParamModifierAttr>()) {
805 if ((PA->isIn() && Spelling == HLSLParamModifierAttr::Keyword_out) ||
806 (PA->isOut() && Spelling == HLSLParamModifierAttr::Keyword_in)) {
807 D->dropAttr<HLSLParamModifierAttr>();
808 SourceRange AdjustedRange = {PA->getLocation(), AL.getRange().getEnd()};
809 return HLSLParamModifierAttr::Create(
810 Ctx&: getASTContext(), /*MergedSpelling=*/true, Range: AdjustedRange,
811 S: HLSLParamModifierAttr::Keyword_inout);
812 }
813 Diag(Loc: AL.getLoc(), DiagID: diag::err_hlsl_duplicate_parameter_modifier) << AL;
814 Diag(Loc: PA->getLocation(), DiagID: diag::note_conflicting_attribute);
815 return nullptr;
816 }
817 return HLSLParamModifierAttr::Create(Ctx&: getASTContext(), CommonInfo: AL);
818}
819
820void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) {
821 auto &TargetInfo = getASTContext().getTargetInfo();
822
823 if (FD->getName() != TargetInfo.getTargetOpts().HLSLEntry)
824 return;
825
826 // If we have specified a root signature to override the entry function then
827 // attach it now
828 HLSLRootSignatureDecl *SignatureDecl =
829 lookupRootSignatureOverrideDecl(DC: FD->getDeclContext());
830 if (SignatureDecl) {
831 FD->dropAttr<RootSignatureAttr>();
832 // We could look up the SourceRange of the macro here as well
833 AttributeCommonInfo AL(RootSigOverrideIdent, AttributeScopeInfo(),
834 SourceRange(), ParsedAttr::Form::Microsoft());
835 FD->addAttr(A: ::new (getASTContext()) RootSignatureAttr(
836 getASTContext(), AL, RootSigOverrideIdent, SignatureDecl));
837 }
838
839 llvm::Triple::EnvironmentType Env = TargetInfo.getTriple().getEnvironment();
840 if (HLSLShaderAttr::isValidShaderType(ShaderType: Env) && Env != llvm::Triple::Library) {
841 if (const auto *Shader = FD->getAttr<HLSLShaderAttr>()) {
842 // The entry point is already annotated - check that it matches the
843 // triple.
844 if (Shader->getType() != Env) {
845 Diag(Loc: Shader->getLocation(), DiagID: diag::err_hlsl_entry_shader_attr_mismatch)
846 << Shader;
847 FD->setInvalidDecl();
848 }
849 } else {
850 // Implicitly add the shader attribute if the entry function isn't
851 // explicitly annotated.
852 FD->addAttr(A: HLSLShaderAttr::CreateImplicit(Ctx&: getASTContext(), Type: Env,
853 Range: FD->getBeginLoc()));
854 }
855 } else {
856 switch (Env) {
857 case llvm::Triple::UnknownEnvironment:
858 case llvm::Triple::Library:
859 break;
860 case llvm::Triple::RootSignature:
861 llvm_unreachable("rootsig environment has no functions");
862 default:
863 llvm_unreachable("Unhandled environment in triple");
864 }
865 }
866}
867
868static bool isVkPipelineBuiltin(const ASTContext &AstContext, FunctionDecl *FD,
869 HLSLAppliedSemanticAttr *Semantic,
870 bool IsInput) {
871 if (AstContext.getTargetInfo().getTriple().getOS() != llvm::Triple::Vulkan)
872 return false;
873
874 const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>();
875 assert(ShaderAttr && "Entry point has no shader attribute");
876 llvm::Triple::EnvironmentType ST = ShaderAttr->getType();
877 SemanticKind Kind = llvm::hlsl::getSemanticKind(SemanticName: Semantic->getSemanticName());
878
879 switch (Kind) {
880 case SemanticKind::Position:
881 // The SV_Position semantic is lowered to:
882 // - Position built-in for vertex output.
883 // - FragCoord built-in for fragment input.
884 return (ST == llvm::Triple::Vertex && !IsInput) ||
885 (ST == llvm::Triple::Pixel && IsInput);
886 case SemanticKind::VertexID:
887 return true;
888 default:
889 return false;
890 }
891}
892
893bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
894 DeclaratorDecl *OutputDecl,
895 DeclaratorDecl *D,
896 SemanticInfo &ActiveSemantic,
897 SemaHLSL::SemanticContext &SC) {
898 if (ActiveSemantic.Semantic == nullptr) {
899 ActiveSemantic.Semantic = D->getAttr<HLSLParsedSemanticAttr>();
900 if (ActiveSemantic.Semantic)
901 ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex();
902 }
903
904 if (!ActiveSemantic.Semantic) {
905 Diag(Loc: D->getLocation(), DiagID: diag::err_hlsl_missing_semantic_annotation);
906 return false;
907 }
908
909 auto *A = ::new (getASTContext())
910 HLSLAppliedSemanticAttr(getASTContext(), *ActiveSemantic.Semantic,
911 ActiveSemantic.Semantic->getAttrName()->getName(),
912 ActiveSemantic.Index.value_or(u: 0));
913 if (!A)
914 return false;
915
916 checkSemanticAnnotation(EntryPoint: FD, Param: D, SemanticAttr: A, SC);
917 OutputDecl->addAttr(A);
918
919 unsigned Location = ActiveSemantic.Index.value_or(u: 0);
920
921 if (!isVkPipelineBuiltin(AstContext: getASTContext(), FD, Semantic: A,
922 IsInput: any(Val: SC.CurrentIOType & IOType::In))) {
923 bool HasVkLocation = false;
924 if (auto *A = D->getAttr<HLSLVkLocationAttr>()) {
925 HasVkLocation = true;
926 Location = A->getLocation();
927 }
928
929 if (SC.UsesExplicitVkLocations.value_or(u&: HasVkLocation) != HasVkLocation) {
930 Diag(Loc: D->getLocation(), DiagID: diag::err_hlsl_semantic_partial_explicit_indexing);
931 return false;
932 }
933 SC.UsesExplicitVkLocations = HasVkLocation;
934 }
935
936 const ConstantArrayType *AT = dyn_cast<ConstantArrayType>(Val: D->getType());
937 unsigned ElementCount = AT ? AT->getZExtSize() : 1;
938 ActiveSemantic.Index = Location + ElementCount;
939
940 Twine BaseName = Twine(ActiveSemantic.Semantic->getAttrName()->getName());
941 for (unsigned I = 0; I < ElementCount; ++I) {
942 Twine VariableName = BaseName.concat(Suffix: Twine(Location + I));
943
944 auto [_, Inserted] = SC.ActiveSemantics.insert(key: VariableName.str());
945 if (!Inserted) {
946 Diag(Loc: D->getLocation(), DiagID: diag::err_hlsl_semantic_index_overlap)
947 << VariableName.str();
948 return false;
949 }
950 }
951
952 return true;
953}
954
955bool SemaHLSL::determineActiveSemantic(FunctionDecl *FD,
956 DeclaratorDecl *OutputDecl,
957 DeclaratorDecl *D,
958 SemanticInfo &ActiveSemantic,
959 SemaHLSL::SemanticContext &SC) {
960 if (ActiveSemantic.Semantic == nullptr) {
961 ActiveSemantic.Semantic = D->getAttr<HLSLParsedSemanticAttr>();
962 if (ActiveSemantic.Semantic)
963 ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex();
964 }
965
966 const Type *T = D == FD ? &*FD->getReturnType() : &*D->getType();
967 T = T->getUnqualifiedDesugaredType();
968
969 const RecordType *RT = dyn_cast<RecordType>(Val: T);
970 if (!RT)
971 return determineActiveSemanticOnScalar(FD, OutputDecl, D, ActiveSemantic,
972 SC);
973
974 const RecordDecl *RD = RT->getDecl();
975 for (FieldDecl *Field : RD->fields()) {
976 SemanticInfo Info = ActiveSemantic;
977 if (!determineActiveSemantic(FD, OutputDecl, D: Field, ActiveSemantic&: Info, SC)) {
978 Diag(Loc: Field->getLocation(), DiagID: diag::note_hlsl_semantic_used_here) << Field;
979 return false;
980 }
981 if (ActiveSemantic.Semantic)
982 ActiveSemantic = Info;
983 }
984
985 return true;
986}
987
988void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
989 const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>();
990 assert(ShaderAttr && "Entry point has no shader attribute");
991 llvm::Triple::EnvironmentType ST = ShaderAttr->getType();
992 auto &TargetInfo = getASTContext().getTargetInfo();
993 VersionTuple Ver = TargetInfo.getTriple().getOSVersion();
994 switch (ST) {
995 case llvm::Triple::Pixel:
996 case llvm::Triple::Vertex:
997 case llvm::Triple::Geometry:
998 case llvm::Triple::Hull:
999 case llvm::Triple::Domain:
1000 case llvm::Triple::RayGeneration:
1001 case llvm::Triple::Intersection:
1002 case llvm::Triple::AnyHit:
1003 case llvm::Triple::ClosestHit:
1004 case llvm::Triple::Miss:
1005 case llvm::Triple::Callable:
1006 if (const auto *NT = FD->getAttr<HLSLNumThreadsAttr>()) {
1007 diagnoseAttrStageMismatch(A: NT, Stage: ST,
1008 AllowedStages: {llvm::Triple::Compute,
1009 llvm::Triple::Amplification,
1010 llvm::Triple::Mesh});
1011 FD->setInvalidDecl();
1012 }
1013 if (const auto *WS = FD->getAttr<HLSLWaveSizeAttr>()) {
1014 diagnoseAttrStageMismatch(A: WS, Stage: ST,
1015 AllowedStages: {llvm::Triple::Compute,
1016 llvm::Triple::Amplification,
1017 llvm::Triple::Mesh});
1018 FD->setInvalidDecl();
1019 }
1020 break;
1021
1022 case llvm::Triple::Compute:
1023 case llvm::Triple::Amplification:
1024 case llvm::Triple::Mesh:
1025 if (!FD->hasAttr<HLSLNumThreadsAttr>()) {
1026 Diag(Loc: FD->getLocation(), DiagID: diag::err_hlsl_missing_numthreads)
1027 << llvm::Triple::getEnvironmentTypeName(Kind: ST);
1028 FD->setInvalidDecl();
1029 }
1030 if (const auto *WS = FD->getAttr<HLSLWaveSizeAttr>()) {
1031 if (TargetInfo.getTriple().isSPIRV()) {
1032 Diag(Loc: WS->getLocation(), DiagID: diag::warn_hlsl_wavesize_unsupported_spirv);
1033 } else if (Ver < VersionTuple(6, 6)) {
1034 Diag(Loc: WS->getLocation(), DiagID: diag::err_hlsl_attribute_in_wrong_shader_model)
1035 << WS << "6.6";
1036 FD->setInvalidDecl();
1037 } else if (WS->getSpelledArgsCount() > 1 && Ver < VersionTuple(6, 8)) {
1038 Diag(
1039 Loc: WS->getLocation(),
1040 DiagID: diag::err_hlsl_attribute_number_arguments_insufficient_shader_model)
1041 << WS << WS->getSpelledArgsCount() << "6.8";
1042 FD->setInvalidDecl();
1043 }
1044 }
1045 break;
1046 case llvm::Triple::RootSignature:
1047 llvm_unreachable("rootsig environment has no function entry point");
1048 default:
1049 llvm_unreachable("Unhandled environment in triple");
1050 }
1051
1052 SemaHLSL::SemanticContext InputSC = {};
1053 InputSC.CurrentIOType = IOType::In;
1054 SemaHLSL::SemanticContext OutputSC = {};
1055 OutputSC.CurrentIOType = IOType::Out;
1056
1057 for (ParmVarDecl *Param : FD->parameters()) {
1058 SemanticInfo ActiveSemantic;
1059 ActiveSemantic.Semantic = Param->getAttr<HLSLParsedSemanticAttr>();
1060 if (ActiveSemantic.Semantic)
1061 ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex();
1062
1063 // FIXME: An `inout` parameter is part of both signatures, but it is only
1064 // verified against the output one here.
1065 const auto *MA = Param->getAttr<HLSLParamModifierAttr>();
1066 SemanticContext &SC = MA && MA->isAnyOut() ? OutputSC : InputSC;
1067
1068 if (!determineActiveSemantic(FD, OutputDecl: Param, D: Param, ActiveSemantic, SC)) {
1069 Diag(Loc: Param->getLocation(), DiagID: diag::note_previous_decl) << Param;
1070 FD->setInvalidDecl();
1071 }
1072 }
1073
1074 SemanticInfo ActiveSemantic;
1075 ActiveSemantic.Semantic = FD->getAttr<HLSLParsedSemanticAttr>();
1076 if (ActiveSemantic.Semantic)
1077 ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex();
1078 if (!FD->getReturnType()->isVoidType())
1079 determineActiveSemantic(FD, OutputDecl: FD, D: FD, ActiveSemantic, SC&: OutputSC);
1080}
1081
1082void SemaHLSL::checkSemanticAnnotation(
1083 FunctionDecl *EntryPoint, const Decl *Param,
1084 const HLSLAppliedSemanticAttr *SemanticAttr, const SemanticContext &SC) {
1085 auto *ShaderAttr = EntryPoint->getAttr<HLSLShaderAttr>();
1086 assert(ShaderAttr && "Entry point has no shader attribute");
1087 llvm::Triple::EnvironmentType ST = ShaderAttr->getType();
1088
1089 SemanticKind Kind =
1090 llvm::hlsl::getSemanticKind(SemanticName: SemanticAttr->getSemanticName());
1091 llvm::hlsl::SemanticInterpretation Interpretation =
1092 llvm::hlsl::getInterpretationKind(SemanticKind: Kind, ShaderStage: ST, IOTy: SC.CurrentIOType);
1093 if (Interpretation == llvm::hlsl::SemanticInterpretation::Invalid)
1094 diagnoseSemanticStageMismatch(A: SemanticAttr, Stage: ST, CurrentIOType: SC.CurrentIOType, SemanticKind: Kind);
1095
1096 switch (Kind) {
1097 case SemanticKind::DispatchThreadID:
1098 case SemanticKind::GroupID:
1099 case SemanticKind::GroupIndex:
1100 case SemanticKind::GroupThreadID:
1101 if (SemanticAttr->getSemanticIndex() != 0) {
1102 std::string PrettyName =
1103 "'" + SemanticAttr->getSemanticName().str() + "'";
1104 Diag(Loc: SemanticAttr->getLoc(),
1105 DiagID: diag::err_hlsl_semantic_indexing_not_supported)
1106 << PrettyName;
1107 }
1108 break;
1109 default:
1110 break;
1111 }
1112}
1113
1114void SemaHLSL::diagnoseAttrStageMismatch(
1115 const Attr *A, llvm::Triple::EnvironmentType Stage,
1116 std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages) {
1117 SmallVector<StringRef, 8> StageStrings;
1118 llvm::transform(Range&: AllowedStages, d_first: std::back_inserter(x&: StageStrings),
1119 F: [](llvm::Triple::EnvironmentType ST) {
1120 return StringRef(
1121 HLSLShaderAttr::ConvertEnvironmentTypeToStr(Val: ST));
1122 });
1123 Diag(Loc: A->getLoc(), DiagID: diag::err_hlsl_attr_unsupported_in_stage)
1124 << A->getAttrName() << llvm::Triple::getEnvironmentTypeName(Kind: Stage)
1125 << (AllowedStages.size() != 1) << join(R&: StageStrings, Separator: ", ");
1126}
1127
1128void SemaHLSL::diagnoseSemanticStageMismatch(
1129 const Attr *A, llvm::Triple::EnvironmentType Stage, IOType CurrentIOType,
1130 SemanticKind Kind) {
1131
1132 ArrayRef<SemanticStageInfo> Allowed = llvm::hlsl::getAvailableStages(SemanticKind: Kind);
1133 auto It = llvm::find_if(Range&: Allowed, P: [&Stage](const SemanticStageInfo &Info) {
1134 return Info.Stage == Stage;
1135 });
1136
1137 StringRef CurrentIOTypeName = "patch constants or primitives";
1138 if (any(Val: CurrentIOType & IOType::In))
1139 CurrentIOTypeName = "inputs";
1140 else if (any(Val: CurrentIOType & IOType::Out))
1141 CurrentIOTypeName = "outputs";
1142
1143 // The semantic is not available in this shader stage at all.
1144 if (It == Allowed.end()) {
1145 Diag(Loc: A->getLoc(), DiagID: diag::err_hlsl_semantic_unsupported_iotype_for_stage)
1146 << A->getAttrName() << llvm::Triple::getEnvironmentTypeName(Kind: Stage)
1147 << CurrentIOTypeName;
1148 return;
1149 }
1150
1151 IOType AllowedIOTypes = It->AllowedIOTypesMask;
1152 if (!(AllowedIOTypes & CurrentIOType)) {
1153 Diag(Loc: A->getLoc(), DiagID: diag::err_hlsl_semantic_unsupported_iotype_for_stage)
1154 << A->getAttrName() << llvm::Triple::getEnvironmentTypeName(Kind: Stage)
1155 << CurrentIOTypeName;
1156 return;
1157 }
1158}
1159
1160template <CastKind Kind>
1161static void castVector(Sema &S, ExprResult &E, QualType &Ty, unsigned Sz) {
1162 if (const auto *VTy = Ty->getAs<VectorType>())
1163 Ty = VTy->getElementType();
1164 Ty = S.getASTContext().getExtVectorType(VectorType: Ty, NumElts: Sz);
1165 E = S.ImpCastExprToType(E: E.get(), Type: Ty, CK: Kind);
1166}
1167
1168template <CastKind Kind>
1169static QualType castElement(Sema &S, ExprResult &E, QualType Ty) {
1170 E = S.ImpCastExprToType(E: E.get(), Type: Ty, CK: Kind);
1171 return Ty;
1172}
1173
1174static QualType handleFloatVectorBinOpConversion(
1175 Sema &SemaRef, ExprResult &LHS, ExprResult &RHS, QualType LHSType,
1176 QualType RHSType, QualType LElTy, QualType RElTy, bool IsCompAssign) {
1177 bool LHSFloat = LElTy->isRealFloatingType();
1178 bool RHSFloat = RElTy->isRealFloatingType();
1179
1180 if (LHSFloat && RHSFloat) {
1181 if (IsCompAssign ||
1182 SemaRef.getASTContext().getFloatingTypeOrder(LHS: LElTy, RHS: RElTy) > 0)
1183 return castElement<CK_FloatingCast>(S&: SemaRef, E&: RHS, Ty: LHSType);
1184
1185 return castElement<CK_FloatingCast>(S&: SemaRef, E&: LHS, Ty: RHSType);
1186 }
1187
1188 if (LHSFloat)
1189 return castElement<CK_IntegralToFloating>(S&: SemaRef, E&: RHS, Ty: LHSType);
1190
1191 assert(RHSFloat);
1192 if (IsCompAssign)
1193 return castElement<clang::CK_FloatingToIntegral>(S&: SemaRef, E&: RHS, Ty: LHSType);
1194
1195 return castElement<CK_IntegralToFloating>(S&: SemaRef, E&: LHS, Ty: RHSType);
1196}
1197
1198static QualType handleIntegerVectorBinOpConversion(
1199 Sema &SemaRef, ExprResult &LHS, ExprResult &RHS, QualType LHSType,
1200 QualType RHSType, QualType LElTy, QualType RElTy, bool IsCompAssign) {
1201
1202 int IntOrder = SemaRef.Context.getIntegerTypeOrder(LHS: LElTy, RHS: RElTy);
1203 bool LHSSigned = LElTy->hasSignedIntegerRepresentation();
1204 bool RHSSigned = RElTy->hasSignedIntegerRepresentation();
1205 auto &Ctx = SemaRef.getASTContext();
1206
1207 // If both types have the same signedness, use the higher ranked type.
1208 if (LHSSigned == RHSSigned) {
1209 if (IsCompAssign || IntOrder >= 0)
1210 return castElement<CK_IntegralCast>(S&: SemaRef, E&: RHS, Ty: LHSType);
1211
1212 return castElement<CK_IntegralCast>(S&: SemaRef, E&: LHS, Ty: RHSType);
1213 }
1214
1215 // If the unsigned type has greater than or equal rank of the signed type, use
1216 // the unsigned type.
1217 if (IntOrder != (LHSSigned ? 1 : -1)) {
1218 if (IsCompAssign || RHSSigned)
1219 return castElement<CK_IntegralCast>(S&: SemaRef, E&: RHS, Ty: LHSType);
1220 return castElement<CK_IntegralCast>(S&: SemaRef, E&: LHS, Ty: RHSType);
1221 }
1222
1223 // At this point the signed type has higher rank than the unsigned type, which
1224 // means it will be the same size or bigger. If the signed type is bigger, it
1225 // can represent all the values of the unsigned type, so select it.
1226 if (Ctx.getIntWidth(T: LElTy) != Ctx.getIntWidth(T: RElTy)) {
1227 if (IsCompAssign || LHSSigned)
1228 return castElement<CK_IntegralCast>(S&: SemaRef, E&: RHS, Ty: LHSType);
1229 return castElement<CK_IntegralCast>(S&: SemaRef, E&: LHS, Ty: RHSType);
1230 }
1231
1232 // This is a bit of an odd duck case in HLSL. It shouldn't happen, but can due
1233 // to C/C++ leaking through. The place this happens today is long vs long
1234 // long. When arguments are vector<unsigned long, N> and vector<long long, N>,
1235 // the long long has higher rank than long even though they are the same size.
1236
1237 // If this is a compound assignment cast the right hand side to the left hand
1238 // side's type.
1239 if (IsCompAssign)
1240 return castElement<CK_IntegralCast>(S&: SemaRef, E&: RHS, Ty: LHSType);
1241
1242 // If this isn't a compound assignment we convert to unsigned long long.
1243 QualType ElTy = Ctx.getCorrespondingUnsignedType(T: LHSSigned ? LElTy : RElTy);
1244 QualType NewTy = Ctx.getExtVectorType(
1245 VectorType: ElTy, NumElts: RHSType->castAs<VectorType>()->getNumElements());
1246 (void)castElement<CK_IntegralCast>(S&: SemaRef, E&: RHS, Ty: NewTy);
1247
1248 return castElement<CK_IntegralCast>(S&: SemaRef, E&: LHS, Ty: NewTy);
1249}
1250
1251static CastKind getScalarCastKind(ASTContext &Ctx, QualType DestTy,
1252 QualType SrcTy) {
1253 if (DestTy->isRealFloatingType() && SrcTy->isRealFloatingType())
1254 return CK_FloatingCast;
1255 if (DestTy->isIntegralType(Ctx) && SrcTy->isIntegralType(Ctx))
1256 return CK_IntegralCast;
1257 if (DestTy->isRealFloatingType())
1258 return CK_IntegralToFloating;
1259 assert(SrcTy->isRealFloatingType() && DestTy->isIntegralType(Ctx));
1260 return CK_FloatingToIntegral;
1261}
1262
1263QualType SemaHLSL::handleVectorBinOpConversion(ExprResult &LHS, ExprResult &RHS,
1264 QualType LHSType,
1265 QualType RHSType,
1266 bool IsCompAssign) {
1267 const auto *LVecTy = LHSType->getAs<VectorType>();
1268 const auto *RVecTy = RHSType->getAs<VectorType>();
1269 auto &Ctx = getASTContext();
1270
1271 // If the LHS is not a vector and this is a compound assignment, we truncate
1272 // the argument to a scalar then convert it to the LHS's type.
1273 if (!LVecTy && IsCompAssign) {
1274 QualType RElTy = RHSType->castAs<VectorType>()->getElementType();
1275 RHS = SemaRef.ImpCastExprToType(E: RHS.get(), Type: RElTy, CK: CK_HLSLVectorTruncation);
1276 RHSType = RHS.get()->getType();
1277 if (Ctx.hasSameUnqualifiedType(T1: LHSType, T2: RHSType))
1278 return LHSType;
1279 RHS = SemaRef.ImpCastExprToType(E: RHS.get(), Type: LHSType,
1280 CK: getScalarCastKind(Ctx, DestTy: LHSType, SrcTy: RHSType));
1281 return LHSType;
1282 }
1283
1284 unsigned EndSz = std::numeric_limits<unsigned>::max();
1285 unsigned LSz = 0;
1286 if (LVecTy)
1287 LSz = EndSz = LVecTy->getNumElements();
1288 if (RVecTy)
1289 EndSz = std::min(a: RVecTy->getNumElements(), b: EndSz);
1290 assert(EndSz != std::numeric_limits<unsigned>::max() &&
1291 "one of the above should have had a value");
1292
1293 // In a compound assignment, the left operand does not change type, the right
1294 // operand is converted to the type of the left operand.
1295 if (IsCompAssign && LSz != EndSz) {
1296 Diag(Loc: LHS.get()->getBeginLoc(),
1297 DiagID: diag::err_hlsl_vector_compound_assignment_truncation)
1298 << LHSType << RHSType;
1299 return QualType();
1300 }
1301
1302 if (RVecTy && RVecTy->getNumElements() > EndSz)
1303 castVector<CK_HLSLVectorTruncation>(S&: SemaRef, E&: RHS, Ty&: RHSType, Sz: EndSz);
1304 if (!IsCompAssign && LVecTy && LVecTy->getNumElements() > EndSz)
1305 castVector<CK_HLSLVectorTruncation>(S&: SemaRef, E&: LHS, Ty&: LHSType, Sz: EndSz);
1306
1307 if (!RVecTy)
1308 castVector<CK_VectorSplat>(S&: SemaRef, E&: RHS, Ty&: RHSType, Sz: EndSz);
1309 if (!IsCompAssign && !LVecTy)
1310 castVector<CK_VectorSplat>(S&: SemaRef, E&: LHS, Ty&: LHSType, Sz: EndSz);
1311
1312 // If we're at the same type after resizing we can stop here.
1313 if (Ctx.hasSameUnqualifiedType(T1: LHSType, T2: RHSType))
1314 return Ctx.getCommonSugaredType(X: LHSType, Y: RHSType);
1315
1316 QualType LElTy = LHSType->castAs<VectorType>()->getElementType();
1317 QualType RElTy = RHSType->castAs<VectorType>()->getElementType();
1318
1319 // Handle conversion for floating point vectors.
1320 if (LElTy->isRealFloatingType() || RElTy->isRealFloatingType())
1321 return handleFloatVectorBinOpConversion(SemaRef, LHS, RHS, LHSType, RHSType,
1322 LElTy, RElTy, IsCompAssign);
1323
1324 assert(LElTy->isIntegralType(Ctx) && RElTy->isIntegralType(Ctx) &&
1325 "HLSL Vectors can only contain integer or floating point types");
1326 return handleIntegerVectorBinOpConversion(SemaRef, LHS, RHS, LHSType, RHSType,
1327 LElTy, RElTy, IsCompAssign);
1328}
1329
1330void SemaHLSL::emitLogicalOperatorFixIt(Expr *LHS, Expr *RHS,
1331 BinaryOperatorKind Opc) {
1332 assert((Opc == BO_LOr || Opc == BO_LAnd) &&
1333 "Called with non-logical operator");
1334 llvm::SmallVector<char, 256> Buff;
1335 llvm::raw_svector_ostream OS(Buff);
1336 PrintingPolicy PP(SemaRef.getLangOpts());
1337 StringRef NewFnName = Opc == BO_LOr ? "or" : "and";
1338 OS << NewFnName << "(";
1339 LHS->printPretty(OS, Helper: nullptr, Policy: PP);
1340 OS << ", ";
1341 RHS->printPretty(OS, Helper: nullptr, Policy: PP);
1342 OS << ")";
1343 SourceRange FullRange = SourceRange(LHS->getBeginLoc(), RHS->getEndLoc());
1344 SemaRef.Diag(Loc: LHS->getBeginLoc(), DiagID: diag::note_function_suggestion)
1345 << NewFnName << FixItHint::CreateReplacement(RemoveRange: FullRange, Code: OS.str());
1346}
1347
1348std::pair<IdentifierInfo *, bool>
1349SemaHLSL::ActOnStartRootSignatureDecl(StringRef Signature) {
1350 llvm::hash_code Hash = llvm::hash_value(S: Signature);
1351 std::string IdStr = "__hlsl_rootsig_decl_" + std::to_string(val: Hash);
1352 IdentifierInfo *DeclIdent = &(getASTContext().Idents.get(Name: IdStr));
1353
1354 // Check if we have already found a decl of the same name.
1355 LookupResult R(SemaRef, DeclIdent, SourceLocation(),
1356 Sema::LookupOrdinaryName);
1357 bool Found = SemaRef.LookupQualifiedName(R, LookupCtx: SemaRef.CurContext);
1358 return {DeclIdent, Found};
1359}
1360
1361void SemaHLSL::ActOnFinishRootSignatureDecl(
1362 SourceLocation Loc, IdentifierInfo *DeclIdent,
1363 ArrayRef<hlsl::RootSignatureElement> RootElements) {
1364
1365 if (handleRootSignatureElements(Elements: RootElements))
1366 return;
1367
1368 SmallVector<llvm::hlsl::rootsig::RootElement> Elements;
1369 for (auto &RootSigElement : RootElements)
1370 Elements.push_back(Elt: RootSigElement.getElement());
1371
1372 auto *SignatureDecl = HLSLRootSignatureDecl::Create(
1373 C&: SemaRef.getASTContext(), /*DeclContext=*/DC: SemaRef.CurContext, Loc,
1374 ID: DeclIdent, Version: SemaRef.getLangOpts().HLSLRootSigVer, RootElements: Elements);
1375
1376 SignatureDecl->setImplicit();
1377 SemaRef.PushOnScopeChains(D: SignatureDecl, S: SemaRef.getCurScope());
1378}
1379
1380HLSLRootSignatureDecl *
1381SemaHLSL::lookupRootSignatureOverrideDecl(DeclContext *DC) const {
1382 if (RootSigOverrideIdent) {
1383 LookupResult R(SemaRef, RootSigOverrideIdent, SourceLocation(),
1384 Sema::LookupOrdinaryName);
1385 if (SemaRef.LookupQualifiedName(R, LookupCtx: DC))
1386 return dyn_cast<HLSLRootSignatureDecl>(Val: R.getFoundDecl());
1387 }
1388
1389 return nullptr;
1390}
1391
1392namespace {
1393
1394struct PerVisibilityBindingChecker {
1395 SemaHLSL *S;
1396 // We need one builder per `llvm::dxbc::ShaderVisibility` value.
1397 std::array<llvm::hlsl::BindingInfoBuilder, 8> Builders;
1398
1399 struct ElemInfo {
1400 const hlsl::RootSignatureElement *Elem;
1401 llvm::dxbc::ShaderVisibility Vis;
1402 bool Diagnosed;
1403 };
1404 llvm::SmallVector<ElemInfo> ElemInfoMap;
1405
1406 PerVisibilityBindingChecker(SemaHLSL *S) : S(S) {}
1407
1408 void trackBinding(llvm::dxbc::ShaderVisibility Visibility,
1409 llvm::dxil::ResourceClass RC, uint32_t Space,
1410 uint32_t LowerBound, uint32_t UpperBound,
1411 const hlsl::RootSignatureElement *Elem) {
1412 uint32_t BuilderIndex = llvm::to_underlying(E: Visibility);
1413 assert(BuilderIndex < Builders.size() &&
1414 "Not enough builders for visibility type");
1415 Builders[BuilderIndex].trackBinding(RC, Space, LowerBound, UpperBound,
1416 Cookie: static_cast<const void *>(Elem));
1417
1418 static_assert(llvm::to_underlying(E: llvm::dxbc::ShaderVisibility::All) == 0,
1419 "'All' visibility must come first");
1420 if (Visibility == llvm::dxbc::ShaderVisibility::All)
1421 for (size_t I = 1, E = Builders.size(); I < E; ++I)
1422 Builders[I].trackBinding(RC, Space, LowerBound, UpperBound,
1423 Cookie: static_cast<const void *>(Elem));
1424
1425 ElemInfoMap.push_back(Elt: {.Elem: Elem, .Vis: Visibility, .Diagnosed: false});
1426 }
1427
1428 ElemInfo &getInfo(const hlsl::RootSignatureElement *Elem) {
1429 auto It = llvm::lower_bound(
1430 Range&: ElemInfoMap, Value&: Elem,
1431 C: [](const auto &LHS, const auto &RHS) { return LHS.Elem < RHS; });
1432 assert(It->Elem == Elem && "Element not in map");
1433 return *It;
1434 }
1435
1436 bool checkOverlap() {
1437 llvm::sort(C&: ElemInfoMap, Comp: [](const auto &LHS, const auto &RHS) {
1438 return LHS.Elem < RHS.Elem;
1439 });
1440
1441 bool HadOverlap = false;
1442
1443 using llvm::hlsl::BindingInfoBuilder;
1444 auto ReportOverlap = [this,
1445 &HadOverlap](const BindingInfoBuilder &Builder,
1446 const llvm::hlsl::Binding &Reported) {
1447 HadOverlap = true;
1448
1449 const auto *Elem =
1450 static_cast<const hlsl::RootSignatureElement *>(Reported.Cookie);
1451 const llvm::hlsl::Binding &Previous = Builder.findOverlapping(ReportedBinding: Reported);
1452 const auto *PrevElem =
1453 static_cast<const hlsl::RootSignatureElement *>(Previous.Cookie);
1454
1455 ElemInfo &Info = getInfo(Elem);
1456 // We will have already diagnosed this binding if there's overlap in the
1457 // "All" visibility as well as any particular visibility.
1458 if (Info.Diagnosed)
1459 return;
1460 Info.Diagnosed = true;
1461
1462 ElemInfo &PrevInfo = getInfo(Elem: PrevElem);
1463 llvm::dxbc::ShaderVisibility CommonVis =
1464 Info.Vis == llvm::dxbc::ShaderVisibility::All ? PrevInfo.Vis
1465 : Info.Vis;
1466
1467 this->S->Diag(Loc: Elem->getLocation(), DiagID: diag::err_hlsl_resource_range_overlap)
1468 << llvm::to_underlying(E: Reported.RC) << Reported.LowerBound
1469 << Reported.isUnbounded() << Reported.UpperBound
1470 << llvm::to_underlying(E: Previous.RC) << Previous.LowerBound
1471 << Previous.isUnbounded() << Previous.UpperBound << Reported.Space
1472 << CommonVis;
1473
1474 this->S->Diag(Loc: PrevElem->getLocation(),
1475 DiagID: diag::note_hlsl_resource_range_here);
1476 };
1477
1478 for (BindingInfoBuilder &Builder : Builders)
1479 Builder.calculateBindingInfo(ReportOverlap);
1480
1481 return HadOverlap;
1482 }
1483};
1484
1485static CXXMethodDecl *lookupMethod(Sema &S, CXXRecordDecl *RecordDecl,
1486 StringRef Name, SourceLocation Loc) {
1487 DeclarationName DeclName(&S.getASTContext().Idents.get(Name));
1488 LookupResult Result(S, DeclName, Loc, Sema::LookupMemberName);
1489 if (!S.LookupQualifiedName(R&: Result, LookupCtx: static_cast<DeclContext *>(RecordDecl)))
1490 return nullptr;
1491 return cast<CXXMethodDecl>(Val: Result.getFoundDecl());
1492}
1493
1494} // end anonymous namespace
1495
1496bool SemaHLSL::handleRootSignatureElements(
1497 ArrayRef<hlsl::RootSignatureElement> Elements) {
1498 // Define some common error handling functions
1499 bool HadError = false;
1500 auto ReportError = [this, &HadError](SourceLocation Loc, uint32_t LowerBound,
1501 uint32_t UpperBound) {
1502 HadError = true;
1503 this->Diag(Loc, DiagID: diag::err_hlsl_invalid_rootsig_value)
1504 << LowerBound << UpperBound;
1505 };
1506
1507 auto ReportFloatError = [this, &HadError](SourceLocation Loc,
1508 float LowerBound,
1509 float UpperBound) {
1510 HadError = true;
1511 this->Diag(Loc, DiagID: diag::err_hlsl_invalid_rootsig_value)
1512 << llvm::formatv(Fmt: "{0:f}", Vals&: LowerBound).sstr<6>()
1513 << llvm::formatv(Fmt: "{0:f}", Vals&: UpperBound).sstr<6>();
1514 };
1515
1516 auto VerifyRegister = [ReportError](SourceLocation Loc, uint32_t Register) {
1517 if (!llvm::hlsl::rootsig::verifyRegisterValue(RegisterValue: Register))
1518 ReportError(Loc, 0, 0xfffffffe);
1519 };
1520
1521 auto VerifySpace = [ReportError](SourceLocation Loc, uint32_t Space) {
1522 if (!llvm::hlsl::rootsig::verifyRegisterSpace(RegisterSpace: Space))
1523 ReportError(Loc, 0, 0xffffffef);
1524 };
1525
1526 const uint32_t Version =
1527 llvm::to_underlying(E: SemaRef.getLangOpts().HLSLRootSigVer);
1528 const uint32_t VersionEnum = Version - 1;
1529 auto ReportFlagError = [this, &HadError, VersionEnum](SourceLocation Loc) {
1530 HadError = true;
1531 this->Diag(Loc, DiagID: diag::err_hlsl_invalid_rootsig_flag)
1532 << /*version minor*/ VersionEnum;
1533 };
1534
1535 // Iterate through the elements and do basic validations
1536 for (const hlsl::RootSignatureElement &RootSigElem : Elements) {
1537 SourceLocation Loc = RootSigElem.getLocation();
1538 const llvm::hlsl::rootsig::RootElement &Elem = RootSigElem.getElement();
1539 if (const auto *Descriptor =
1540 std::get_if<llvm::hlsl::rootsig::RootDescriptor>(ptr: &Elem)) {
1541 VerifyRegister(Loc, Descriptor->Reg.Number);
1542 VerifySpace(Loc, Descriptor->Space);
1543
1544 if (!llvm::hlsl::rootsig::verifyRootDescriptorFlag(Version,
1545 Flags: Descriptor->Flags))
1546 ReportFlagError(Loc);
1547 } else if (const auto *Constants =
1548 std::get_if<llvm::hlsl::rootsig::RootConstants>(ptr: &Elem)) {
1549 VerifyRegister(Loc, Constants->Reg.Number);
1550 VerifySpace(Loc, Constants->Space);
1551 } else if (const auto *Sampler =
1552 std::get_if<llvm::hlsl::rootsig::StaticSampler>(ptr: &Elem)) {
1553 VerifyRegister(Loc, Sampler->Reg.Number);
1554 VerifySpace(Loc, Sampler->Space);
1555
1556 assert(!std::isnan(Sampler->MaxLOD) && !std::isnan(Sampler->MinLOD) &&
1557 "By construction, parseFloatParam can't produce a NaN from a "
1558 "float_literal token");
1559
1560 if (!llvm::hlsl::rootsig::verifyMaxAnisotropy(MaxAnisotropy: Sampler->MaxAnisotropy))
1561 ReportError(Loc, 0, 16);
1562 if (!llvm::hlsl::rootsig::verifyMipLODBias(MipLODBias: Sampler->MipLODBias))
1563 ReportFloatError(Loc, -16.f, 15.99f);
1564 } else if (const auto *Clause =
1565 std::get_if<llvm::hlsl::rootsig::DescriptorTableClause>(
1566 ptr: &Elem)) {
1567 VerifyRegister(Loc, Clause->Reg.Number);
1568 VerifySpace(Loc, Clause->Space);
1569
1570 if (!llvm::hlsl::rootsig::verifyNumDescriptors(NumDescriptors: Clause->NumDescriptors)) {
1571 // NumDescriptor could techincally be ~0u but that is reserved for
1572 // unbounded, so the diagnostic will not report that as a valid int
1573 // value
1574 ReportError(Loc, 1, 0xfffffffe);
1575 }
1576
1577 if (!llvm::hlsl::rootsig::verifyDescriptorRangeFlag(Version, Type: Clause->Type,
1578 Flags: Clause->Flags))
1579 ReportFlagError(Loc);
1580 }
1581 }
1582
1583 PerVisibilityBindingChecker BindingChecker(this);
1584 SmallVector<std::pair<const llvm::hlsl::rootsig::DescriptorTableClause *,
1585 const hlsl::RootSignatureElement *>>
1586 UnboundClauses;
1587
1588 for (const hlsl::RootSignatureElement &RootSigElem : Elements) {
1589 const llvm::hlsl::rootsig::RootElement &Elem = RootSigElem.getElement();
1590 if (const auto *Descriptor =
1591 std::get_if<llvm::hlsl::rootsig::RootDescriptor>(ptr: &Elem)) {
1592 uint32_t LowerBound(Descriptor->Reg.Number);
1593 uint32_t UpperBound(LowerBound); // inclusive range
1594
1595 BindingChecker.trackBinding(
1596 Visibility: Descriptor->Visibility,
1597 RC: static_cast<llvm::dxil::ResourceClass>(Descriptor->Type),
1598 Space: Descriptor->Space, LowerBound, UpperBound, Elem: &RootSigElem);
1599 } else if (const auto *Constants =
1600 std::get_if<llvm::hlsl::rootsig::RootConstants>(ptr: &Elem)) {
1601 uint32_t LowerBound(Constants->Reg.Number);
1602 uint32_t UpperBound(LowerBound); // inclusive range
1603
1604 BindingChecker.trackBinding(
1605 Visibility: Constants->Visibility, RC: llvm::dxil::ResourceClass::CBuffer,
1606 Space: Constants->Space, LowerBound, UpperBound, Elem: &RootSigElem);
1607 } else if (const auto *Sampler =
1608 std::get_if<llvm::hlsl::rootsig::StaticSampler>(ptr: &Elem)) {
1609 uint32_t LowerBound(Sampler->Reg.Number);
1610 uint32_t UpperBound(LowerBound); // inclusive range
1611
1612 BindingChecker.trackBinding(
1613 Visibility: Sampler->Visibility, RC: llvm::dxil::ResourceClass::Sampler,
1614 Space: Sampler->Space, LowerBound, UpperBound, Elem: &RootSigElem);
1615 } else if (const auto *Clause =
1616 std::get_if<llvm::hlsl::rootsig::DescriptorTableClause>(
1617 ptr: &Elem)) {
1618 // We'll process these once we see the table element.
1619 UnboundClauses.emplace_back(Args&: Clause, Args: &RootSigElem);
1620 } else if (const auto *Table =
1621 std::get_if<llvm::hlsl::rootsig::DescriptorTable>(ptr: &Elem)) {
1622 assert(UnboundClauses.size() == Table->NumClauses &&
1623 "Number of unbound elements must match the number of clauses");
1624 bool HasAnySampler = false;
1625 bool HasAnyNonSampler = false;
1626 uint64_t Offset = 0;
1627 bool IsPrevUnbound = false;
1628 for (const auto &[Clause, ClauseElem] : UnboundClauses) {
1629 SourceLocation Loc = ClauseElem->getLocation();
1630 if (Clause->Type == llvm::dxil::ResourceClass::Sampler)
1631 HasAnySampler = true;
1632 else
1633 HasAnyNonSampler = true;
1634
1635 if (HasAnySampler && HasAnyNonSampler)
1636 Diag(Loc, DiagID: diag::err_hlsl_invalid_mixed_resources);
1637
1638 // Relevant error will have already been reported above and needs to be
1639 // fixed before we can conduct further analysis, so shortcut error
1640 // return
1641 if (Clause->NumDescriptors == 0)
1642 return true;
1643
1644 bool IsAppending =
1645 Clause->Offset == llvm::hlsl::rootsig::DescriptorTableOffsetAppend;
1646 if (!IsAppending)
1647 Offset = Clause->Offset;
1648
1649 uint64_t RangeBound = llvm::hlsl::rootsig::computeRangeBound(
1650 Offset, Size: Clause->NumDescriptors);
1651
1652 if (IsPrevUnbound && IsAppending)
1653 Diag(Loc, DiagID: diag::err_hlsl_appending_onto_unbound);
1654 else if (!llvm::hlsl::rootsig::verifyNoOverflowedOffset(Offset: RangeBound))
1655 Diag(Loc, DiagID: diag::err_hlsl_offset_overflow) << Offset << RangeBound;
1656
1657 // Update offset to be 1 past this range's bound
1658 Offset = RangeBound + 1;
1659 IsPrevUnbound = Clause->NumDescriptors ==
1660 llvm::hlsl::rootsig::NumDescriptorsUnbounded;
1661
1662 // Compute the register bounds and track resource binding
1663 uint32_t LowerBound(Clause->Reg.Number);
1664 uint32_t UpperBound = llvm::hlsl::rootsig::computeRangeBound(
1665 Offset: LowerBound, Size: Clause->NumDescriptors);
1666
1667 BindingChecker.trackBinding(
1668 Visibility: Table->Visibility,
1669 RC: static_cast<llvm::dxil::ResourceClass>(Clause->Type), Space: Clause->Space,
1670 LowerBound, UpperBound, Elem: ClauseElem);
1671 }
1672 UnboundClauses.clear();
1673 }
1674 }
1675
1676 return BindingChecker.checkOverlap();
1677}
1678
1679void SemaHLSL::handleRootSignatureAttr(Decl *D, const ParsedAttr &AL) {
1680 if (AL.getNumArgs() != 1) {
1681 Diag(Loc: AL.getLoc(), DiagID: diag::err_attribute_wrong_number_arguments) << AL << 1;
1682 return;
1683 }
1684
1685 IdentifierInfo *Ident = AL.getArgAsIdent(Arg: 0)->getIdentifierInfo();
1686 if (auto *RS = D->getAttr<RootSignatureAttr>()) {
1687 if (RS->getSignatureIdent() != Ident) {
1688 Diag(Loc: AL.getLoc(), DiagID: diag::err_disallowed_duplicate_attribute) << RS;
1689 return;
1690 }
1691
1692 Diag(Loc: AL.getLoc(), DiagID: diag::warn_duplicate_attribute_exact) << RS;
1693 return;
1694 }
1695
1696 LookupResult R(SemaRef, Ident, SourceLocation(), Sema::LookupOrdinaryName);
1697 if (SemaRef.LookupQualifiedName(R, LookupCtx: D->getDeclContext()))
1698 if (auto *SignatureDecl =
1699 dyn_cast<HLSLRootSignatureDecl>(Val: R.getFoundDecl())) {
1700 D->addAttr(A: ::new (getASTContext()) RootSignatureAttr(
1701 getASTContext(), AL, Ident, SignatureDecl));
1702 }
1703}
1704
1705void SemaHLSL::handleNumThreadsAttr(Decl *D, const ParsedAttr &AL) {
1706 llvm::VersionTuple SMVersion =
1707 getASTContext().getTargetInfo().getTriple().getOSVersion();
1708 bool IsDXIL = getASTContext().getTargetInfo().getTriple().getArch() ==
1709 llvm::Triple::dxil;
1710
1711 uint32_t ZMax = 1024;
1712 uint32_t ThreadMax = 1024;
1713 if (IsDXIL && SMVersion.getMajor() <= 4) {
1714 ZMax = 1;
1715 ThreadMax = 768;
1716 } else if (IsDXIL && SMVersion.getMajor() == 5) {
1717 ZMax = 64;
1718 ThreadMax = 1024;
1719 }
1720
1721 uint32_t X;
1722 if (!SemaRef.checkUInt32Argument(AI: AL, Expr: AL.getArgAsExpr(Arg: 0), Val&: X))
1723 return;
1724 if (X > 1024) {
1725 Diag(Loc: AL.getArgAsExpr(Arg: 0)->getExprLoc(),
1726 DiagID: diag::err_hlsl_numthreads_argument_oor)
1727 << 0 << 1024;
1728 return;
1729 }
1730 uint32_t Y;
1731 if (!SemaRef.checkUInt32Argument(AI: AL, Expr: AL.getArgAsExpr(Arg: 1), Val&: Y))
1732 return;
1733 if (Y > 1024) {
1734 Diag(Loc: AL.getArgAsExpr(Arg: 1)->getExprLoc(),
1735 DiagID: diag::err_hlsl_numthreads_argument_oor)
1736 << 1 << 1024;
1737 return;
1738 }
1739 uint32_t Z;
1740 if (!SemaRef.checkUInt32Argument(AI: AL, Expr: AL.getArgAsExpr(Arg: 2), Val&: Z))
1741 return;
1742 if (Z > ZMax) {
1743 SemaRef.Diag(Loc: AL.getArgAsExpr(Arg: 2)->getExprLoc(),
1744 DiagID: diag::err_hlsl_numthreads_argument_oor)
1745 << 2 << ZMax;
1746 return;
1747 }
1748
1749 if (X * Y * Z > ThreadMax) {
1750 Diag(Loc: AL.getLoc(), DiagID: diag::err_hlsl_numthreads_invalid) << ThreadMax;
1751 return;
1752 }
1753
1754 HLSLNumThreadsAttr *NewAttr = mergeNumThreadsAttr(D, AL, X, Y, Z);
1755 if (NewAttr)
1756 D->addAttr(A: NewAttr);
1757}
1758
1759static bool isValidWaveSizeValue(unsigned Value) {
1760 return llvm::isPowerOf2_32(Value) && Value >= 4 && Value <= 128;
1761}
1762
1763void SemaHLSL::handleWaveSizeAttr(Decl *D, const ParsedAttr &AL) {
1764 // validate that the wavesize argument is a power of 2 between 4 and 128
1765 // inclusive
1766 unsigned SpelledArgsCount = AL.getNumArgs();
1767 if (SpelledArgsCount == 0 || SpelledArgsCount > 3)
1768 return;
1769
1770 uint32_t Min;
1771 if (!SemaRef.checkUInt32Argument(AI: AL, Expr: AL.getArgAsExpr(Arg: 0), Val&: Min))
1772 return;
1773
1774 uint32_t Max = 0;
1775 if (SpelledArgsCount > 1 &&
1776 !SemaRef.checkUInt32Argument(AI: AL, Expr: AL.getArgAsExpr(Arg: 1), Val&: Max))
1777 return;
1778
1779 uint32_t Preferred = 0;
1780 if (SpelledArgsCount > 2 &&
1781 !SemaRef.checkUInt32Argument(AI: AL, Expr: AL.getArgAsExpr(Arg: 2), Val&: Preferred))
1782 return;
1783
1784 if (SpelledArgsCount > 2) {
1785 if (!isValidWaveSizeValue(Value: Preferred)) {
1786 Diag(Loc: AL.getArgAsExpr(Arg: 2)->getExprLoc(),
1787 DiagID: diag::err_attribute_power_of_two_in_range)
1788 << AL << llvm::dxil::MinWaveSize << llvm::dxil::MaxWaveSize
1789 << Preferred;
1790 return;
1791 }
1792 // Preferred not in range.
1793 if (Preferred < Min || Preferred > Max) {
1794 Diag(Loc: AL.getArgAsExpr(Arg: 2)->getExprLoc(),
1795 DiagID: diag::err_attribute_power_of_two_in_range)
1796 << AL << Min << Max << Preferred;
1797 return;
1798 }
1799 } else if (SpelledArgsCount > 1) {
1800 if (!isValidWaveSizeValue(Value: Max)) {
1801 Diag(Loc: AL.getArgAsExpr(Arg: 1)->getExprLoc(),
1802 DiagID: diag::err_attribute_power_of_two_in_range)
1803 << AL << llvm::dxil::MinWaveSize << llvm::dxil::MaxWaveSize << Max;
1804 return;
1805 }
1806 if (Max < Min) {
1807 Diag(Loc: AL.getLoc(), DiagID: diag::err_attribute_argument_invalid) << AL << 1;
1808 return;
1809 } else if (Max == Min) {
1810 Diag(Loc: AL.getLoc(), DiagID: diag::warn_attr_min_eq_max) << AL;
1811 }
1812 } else {
1813 if (!isValidWaveSizeValue(Value: Min)) {
1814 Diag(Loc: AL.getArgAsExpr(Arg: 0)->getExprLoc(),
1815 DiagID: diag::err_attribute_power_of_two_in_range)
1816 << AL << llvm::dxil::MinWaveSize << llvm::dxil::MaxWaveSize << Min;
1817 return;
1818 }
1819 }
1820
1821 HLSLWaveSizeAttr *NewAttr =
1822 mergeWaveSizeAttr(D, AL, Min, Max, Preferred, SpelledArgsCount);
1823 if (NewAttr)
1824 D->addAttr(A: NewAttr);
1825}
1826
1827void SemaHLSL::handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL) {
1828 uint32_t ID;
1829 if (!SemaRef.checkUInt32Argument(AI: AL, Expr: AL.getArgAsExpr(Arg: 0), Val&: ID))
1830 return;
1831 D->addAttr(A: ::new (getASTContext())
1832 HLSLVkExtBuiltinInputAttr(getASTContext(), AL, ID));
1833}
1834
1835void SemaHLSL::handleVkExtBuiltinOutputAttr(Decl *D, const ParsedAttr &AL) {
1836 uint32_t ID;
1837 if (!SemaRef.checkUInt32Argument(AI: AL, Expr: AL.getArgAsExpr(Arg: 0), Val&: ID))
1838 return;
1839 D->addAttr(A: ::new (getASTContext())
1840 HLSLVkExtBuiltinOutputAttr(getASTContext(), AL, ID));
1841}
1842
1843void SemaHLSL::handleVkPushConstantAttr(Decl *D, const ParsedAttr &AL) {
1844 D->addAttr(A: ::new (getASTContext())
1845 HLSLVkPushConstantAttr(getASTContext(), AL));
1846}
1847
1848void SemaHLSL::handleVkConstantIdAttr(Decl *D, const ParsedAttr &AL) {
1849 uint32_t Id;
1850 if (!SemaRef.checkUInt32Argument(AI: AL, Expr: AL.getArgAsExpr(Arg: 0), Val&: Id))
1851 return;
1852 HLSLVkConstantIdAttr *NewAttr = mergeVkConstantIdAttr(D, AL, Id);
1853 if (NewAttr)
1854 D->addAttr(A: NewAttr);
1855}
1856
1857void SemaHLSL::handleVkBindingAttr(Decl *D, const ParsedAttr &AL) {
1858 uint32_t Binding = 0;
1859 if (!SemaRef.checkUInt32Argument(AI: AL, Expr: AL.getArgAsExpr(Arg: 0), Val&: Binding))
1860 return;
1861 uint32_t Set = 0;
1862 if (AL.getNumArgs() > 1 &&
1863 !SemaRef.checkUInt32Argument(AI: AL, Expr: AL.getArgAsExpr(Arg: 1), Val&: Set))
1864 return;
1865
1866 D->addAttr(A: ::new (getASTContext())
1867 HLSLVkBindingAttr(getASTContext(), AL, Binding, Set));
1868}
1869
1870void SemaHLSL::handleVkLocationAttr(Decl *D, const ParsedAttr &AL) {
1871 uint32_t Location;
1872 if (!SemaRef.checkUInt32Argument(AI: AL, Expr: AL.getArgAsExpr(Arg: 0), Val&: Location))
1873 return;
1874
1875 D->addAttr(A: ::new (getASTContext())
1876 HLSLVkLocationAttr(getASTContext(), AL, Location));
1877}
1878
1879bool SemaHLSL::diagnoseIndexType(QualType T, const ParsedAttr &AL) {
1880 const auto *VT = T->getAs<VectorType>();
1881
1882 if (!T->hasUnsignedIntegerRepresentation() ||
1883 (VT && VT->getNumElements() > 3)) {
1884 Diag(Loc: AL.getLoc(), DiagID: diag::err_hlsl_attr_invalid_type)
1885 << AL << "uint/uint2/uint3";
1886 return false;
1887 }
1888
1889 return true;
1890}
1891
1892bool SemaHLSL::diagnoseFloatType(QualType T, const ParsedAttr &AL) {
1893 const auto *VT = T->getAs<VectorType>();
1894 if (!T->hasFloatingRepresentation() || (VT && VT->getNumElements() > 4)) {
1895 Diag(Loc: AL.getLoc(), DiagID: diag::err_hlsl_attr_invalid_type)
1896 << AL << "float/float1/float2/float3/float4";
1897 return false;
1898 }
1899
1900 return true;
1901}
1902
1903void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL,
1904 SemanticKind Kind,
1905 std::optional<unsigned> Index) {
1906 auto *VD = cast<ValueDecl>(Val: D);
1907 QualType ValueType = VD->getType();
1908 if (auto *FD = dyn_cast<FunctionDecl>(Val: D))
1909 ValueType = FD->getReturnType();
1910
1911 // `out` and `inout` parameters are passed by reference.
1912 if (HLSLParamModifierAttr *MA = D->getAttr<HLSLParamModifierAttr>())
1913 if (MA->isAnyOut())
1914 ValueType = cast<ReferenceType>(Val&: ValueType)->getPointeeType();
1915
1916 switch (Kind) {
1917 case SemanticKind::DispatchThreadID:
1918 case SemanticKind::GroupThreadID:
1919 case SemanticKind::GroupID:
1920 diagnoseIndexType(T: ValueType, AL);
1921 break;
1922 case SemanticKind::GroupIndex:
1923 break;
1924 case SemanticKind::Position:
1925 case SemanticKind::Target:
1926 diagnoseFloatType(T: ValueType, AL);
1927 break;
1928 case SemanticKind::VertexID: {
1929 uint64_t SizeInBits = SemaRef.Context.getTypeSize(T: ValueType);
1930 if (!ValueType->isUnsignedIntegerType() || SizeInBits != 32)
1931 Diag(Loc: AL.getLoc(), DiagID: diag::err_hlsl_attr_invalid_type) << AL << "uint";
1932 break;
1933 }
1934 default:
1935 Diag(Loc: AL.getLoc(), DiagID: diag::err_hlsl_unknown_semantic) << AL;
1936 return;
1937 }
1938
1939 D->addAttr(A: createSemanticAttr<HLSLParsedSemanticAttr>(ACI: AL, Location: Index));
1940}
1941
1942void SemaHLSL::handleSemanticAttr(Decl *D, const ParsedAttr &AL) {
1943 uint32_t IndexValue(0), ExplicitIndex(0);
1944 if (!SemaRef.checkUInt32Argument(AI: AL, Expr: AL.getArgAsExpr(Arg: 0), Val&: IndexValue) ||
1945 !SemaRef.checkUInt32Argument(AI: AL, Expr: AL.getArgAsExpr(Arg: 1), Val&: ExplicitIndex)) {
1946 assert(0 && "HLSLUnparsedSemantic is expected to have 2 int arguments.");
1947 }
1948 assert(IndexValue > 0 ? ExplicitIndex : true);
1949 std::optional<unsigned> Index =
1950 ExplicitIndex ? std::optional<unsigned>(IndexValue) : std::nullopt;
1951
1952 SemanticKind Kind = llvm::hlsl::getSemanticKind(SemanticName: AL.getAttrName()->getName());
1953 if (Kind == SemanticKind::Arbitrary)
1954 D->addAttr(A: createSemanticAttr<HLSLParsedSemanticAttr>(ACI: AL, Location: Index));
1955 else
1956 diagnoseSystemSemanticAttr(D, AL, Kind, Index);
1957}
1958
1959void SemaHLSL::handlePackOffsetAttr(Decl *D, const ParsedAttr &AL) {
1960 if (!isa<VarDecl>(Val: D) || !isa<HLSLBufferDecl>(Val: D->getDeclContext())) {
1961 Diag(Loc: AL.getLoc(), DiagID: diag::err_hlsl_attr_invalid_ast_node)
1962 << AL << "shader constant in a constant buffer";
1963 return;
1964 }
1965
1966 uint32_t SubComponent;
1967 if (!SemaRef.checkUInt32Argument(AI: AL, Expr: AL.getArgAsExpr(Arg: 0), Val&: SubComponent))
1968 return;
1969 uint32_t Component;
1970 if (!SemaRef.checkUInt32Argument(AI: AL, Expr: AL.getArgAsExpr(Arg: 1), Val&: Component))
1971 return;
1972
1973 QualType T = cast<VarDecl>(Val: D)->getType().getCanonicalType();
1974 // Check if T is an array or struct type.
1975 // TODO: mark matrix type as aggregate type.
1976 bool IsAggregateTy = (T->isArrayType() || T->isStructureType());
1977
1978 // Check Component is valid for T.
1979 if (Component) {
1980 unsigned Size = getASTContext().getTypeSize(T);
1981 if (IsAggregateTy) {
1982 Diag(Loc: AL.getLoc(), DiagID: diag::err_hlsl_invalid_register_or_packoffset);
1983 return;
1984 } else {
1985 // Make sure Component + sizeof(T) <= 4.
1986 if ((Component * 32 + Size) > 128) {
1987 Diag(Loc: AL.getLoc(), DiagID: diag::err_hlsl_packoffset_cross_reg_boundary);
1988 return;
1989 }
1990 QualType EltTy = T;
1991 if (const auto *VT = T->getAs<VectorType>())
1992 EltTy = VT->getElementType();
1993 unsigned Align = getASTContext().getTypeAlign(T: EltTy);
1994 if (Align > 32 && Component == 1) {
1995 // NOTE: Component 3 will hit err_hlsl_packoffset_cross_reg_boundary.
1996 // So we only need to check Component 1 here.
1997 Diag(Loc: AL.getLoc(), DiagID: diag::err_hlsl_packoffset_alignment_mismatch)
1998 << Align << EltTy;
1999 return;
2000 }
2001 }
2002 }
2003
2004 D->addAttr(A: ::new (getASTContext()) HLSLPackOffsetAttr(
2005 getASTContext(), AL, SubComponent, Component));
2006}
2007
2008void SemaHLSL::handleShaderAttr(Decl *D, const ParsedAttr &AL) {
2009 StringRef Str;
2010 SourceLocation ArgLoc;
2011 if (!SemaRef.checkStringLiteralArgumentAttr(Attr: AL, ArgNum: 0, Str, ArgLocation: &ArgLoc))
2012 return;
2013
2014 llvm::Triple::EnvironmentType ShaderType;
2015 if (!HLSLShaderAttr::ConvertStrToEnvironmentType(Val: Str, Out&: ShaderType)) {
2016 Diag(Loc: AL.getLoc(), DiagID: diag::warn_attribute_type_not_supported)
2017 << AL << Str << ArgLoc;
2018 return;
2019 }
2020
2021 // FIXME: check function match the shader stage.
2022
2023 HLSLShaderAttr *NewAttr = mergeShaderAttr(D, AL, ShaderType);
2024 if (NewAttr)
2025 D->addAttr(A: NewAttr);
2026}
2027
2028bool clang::CreateHLSLAttributedResourceType(
2029 Sema &S, QualType Wrapped, ArrayRef<const Attr *> AttrList,
2030 QualType &ResType, HLSLAttributedResourceLocInfo *LocInfo,
2031 Expr *SampleCountExpr) {
2032 assert(AttrList.size() && "expected list of resource attributes");
2033
2034 QualType ContainedTy = QualType();
2035 TypeSourceInfo *ContainedTyInfo = nullptr;
2036 SourceLocation LocBegin = AttrList[0]->getRange().getBegin();
2037 SourceLocation LocEnd = AttrList[0]->getRange().getEnd();
2038
2039 HLSLAttributedResourceType::Attributes ResAttrs;
2040
2041 bool HasResourceClass = false;
2042 bool HasResourceDimension = false;
2043 for (const Attr *A : AttrList) {
2044 if (!A)
2045 continue;
2046 LocEnd = A->getRange().getEnd();
2047 switch (A->getKind()) {
2048 case attr::HLSLResourceClass: {
2049 ResourceClass RC = cast<HLSLResourceClassAttr>(Val: A)->getResourceClass();
2050 if (HasResourceClass) {
2051 S.Diag(Loc: A->getLocation(), DiagID: ResAttrs.ResourceClass == RC
2052 ? diag::warn_duplicate_attribute_exact
2053 : diag::warn_duplicate_attribute)
2054 << A;
2055 return false;
2056 }
2057 ResAttrs.ResourceClass = RC;
2058 HasResourceClass = true;
2059 break;
2060 }
2061 case attr::HLSLResourceDimension: {
2062 llvm::dxil::ResourceDimension RD =
2063 cast<HLSLResourceDimensionAttr>(Val: A)->getDimension();
2064 if (HasResourceDimension) {
2065 S.Diag(Loc: A->getLocation(), DiagID: ResAttrs.ResourceDimension == RD
2066 ? diag::warn_duplicate_attribute_exact
2067 : diag::warn_duplicate_attribute)
2068 << A;
2069 return false;
2070 }
2071 ResAttrs.ResourceDimension = RD;
2072 HasResourceDimension = true;
2073 break;
2074 }
2075 case attr::HLSLIsROV:
2076 if (ResAttrs.IsROV) {
2077 S.Diag(Loc: A->getLocation(), DiagID: diag::warn_duplicate_attribute_exact) << A;
2078 return false;
2079 }
2080 ResAttrs.IsROV = true;
2081 break;
2082 case attr::HLSLRawBuffer:
2083 if (ResAttrs.RawBuffer) {
2084 S.Diag(Loc: A->getLocation(), DiagID: diag::warn_duplicate_attribute_exact) << A;
2085 return false;
2086 }
2087 ResAttrs.RawBuffer = true;
2088 break;
2089 case attr::HLSLIsArray:
2090 if (ResAttrs.IsArray) {
2091 S.Diag(Loc: A->getLocation(), DiagID: diag::warn_duplicate_attribute_exact) << A;
2092 return false;
2093 }
2094 ResAttrs.IsArray = true;
2095 break;
2096 case attr::HLSLIsMultiSampled:
2097 if (ResAttrs.SampleCountExpr) {
2098 S.Diag(Loc: A->getLocation(), DiagID: diag::warn_duplicate_attribute_exact) << A;
2099 return false;
2100 }
2101 // A bare [[hlsl::is_ms]] carries no count, so default it to 0, the same
2102 // value Texture2DMS<T> gets from its template parameter.
2103 ResAttrs.SampleCountExpr =
2104 SampleCountExpr
2105 ? SampleCountExpr
2106 : IntegerLiteral::Create(C: S.Context, V: llvm::APInt(32, 0),
2107 type: S.Context.IntTy, l: A->getLocation());
2108 break;
2109 case attr::HLSLIsCounter:
2110 if (ResAttrs.IsCounter) {
2111 S.Diag(Loc: A->getLocation(), DiagID: diag::warn_duplicate_attribute_exact) << A;
2112 return false;
2113 }
2114 ResAttrs.IsCounter = true;
2115 break;
2116 case attr::HLSLContainedType: {
2117 const HLSLContainedTypeAttr *CTAttr = cast<HLSLContainedTypeAttr>(Val: A);
2118 QualType Ty = CTAttr->getType();
2119 if (!ContainedTy.isNull()) {
2120 S.Diag(Loc: A->getLocation(), DiagID: ContainedTy == Ty
2121 ? diag::warn_duplicate_attribute_exact
2122 : diag::warn_duplicate_attribute)
2123 << A;
2124 return false;
2125 }
2126 ContainedTy = Ty;
2127 ContainedTyInfo = CTAttr->getTypeLoc();
2128 break;
2129 }
2130 default:
2131 llvm_unreachable("unhandled resource attribute type");
2132 }
2133 }
2134
2135 if (!HasResourceClass) {
2136 S.Diag(Loc: AttrList.back()->getRange().getEnd(),
2137 DiagID: diag::err_hlsl_missing_resource_class);
2138 return false;
2139 }
2140
2141 ResType = S.getASTContext().getHLSLAttributedResourceType(
2142 Wrapped, Contained: ContainedTy, Attrs: ResAttrs);
2143
2144 if (LocInfo && ContainedTyInfo) {
2145 LocInfo->Range = SourceRange(LocBegin, LocEnd);
2146 LocInfo->ContainedTyInfo = ContainedTyInfo;
2147 }
2148 return true;
2149}
2150
2151// Validates and creates an HLSL attribute that is applied as type attribute on
2152// HLSL resource. The attributes are collected in HLSLResourcesTypeAttrs and at
2153// the end of the declaration they are applied to the declaration type by
2154// wrapping it in HLSLAttributedResourceType.
2155bool SemaHLSL::handleResourceTypeAttr(QualType T, const ParsedAttr &AL) {
2156 // only allow resource type attributes on intangible types
2157 if (!T->isHLSLResourceType()) {
2158 Diag(Loc: AL.getLoc(), DiagID: diag::err_hlsl_attribute_needs_intangible_type)
2159 << AL << getASTContext().HLSLResourceTy;
2160 return false;
2161 }
2162
2163 // validate number of arguments
2164 if (!AL.checkExactlyNumArgs(S&: SemaRef, Num: AL.getMinArgs()))
2165 return false;
2166
2167 Attr *A = nullptr;
2168
2169 AttributeCommonInfo ACI(
2170 AL.getLoc(), AttributeScopeInfo(AL.getScopeName(), AL.getScopeLoc()),
2171 AttributeCommonInfo::NoSemaHandlerAttribute,
2172 {
2173 AttributeCommonInfo::AS_CXX11, 0, false /*IsAlignas*/,
2174 false /*IsRegularKeywordAttribute*/
2175 });
2176
2177 switch (AL.getKind()) {
2178 case ParsedAttr::AT_HLSLResourceClass: {
2179 StringRef Identifier;
2180 SourceLocation ArgLoc;
2181 if (!SemaRef.checkStringLiteralArgumentAttr(Attr: AL, ArgNum: 0, Str&: Identifier, ArgLocation: &ArgLoc))
2182 return false;
2183
2184 // Validate resource class value
2185 ResourceClass RC;
2186 if (!HLSLResourceClassAttr::ConvertStrToResourceClass(Val: Identifier, Out&: RC)) {
2187 Diag(Loc: ArgLoc, DiagID: diag::warn_attribute_type_not_supported)
2188 << "ResourceClass" << Identifier;
2189 return false;
2190 }
2191 A = HLSLResourceClassAttr::Create(Ctx&: getASTContext(), ResourceClass: RC, CommonInfo: ACI);
2192 break;
2193 }
2194
2195 case ParsedAttr::AT_HLSLResourceDimension: {
2196 StringRef Identifier;
2197 SourceLocation ArgLoc;
2198 if (!SemaRef.checkStringLiteralArgumentAttr(Attr: AL, ArgNum: 0, Str&: Identifier, ArgLocation: &ArgLoc))
2199 return false;
2200
2201 // Validate resource dimension value
2202 llvm::dxil::ResourceDimension RD;
2203 if (!HLSLResourceDimensionAttr::ConvertStrToResourceDimension(Val: Identifier,
2204 Out&: RD)) {
2205 Diag(Loc: ArgLoc, DiagID: diag::warn_attribute_type_not_supported)
2206 << "ResourceDimension" << Identifier;
2207 return false;
2208 }
2209 A = HLSLResourceDimensionAttr::Create(Ctx&: getASTContext(), Dimension: RD, CommonInfo: ACI);
2210 break;
2211 }
2212
2213 case ParsedAttr::AT_HLSLIsROV:
2214 A = HLSLIsROVAttr::Create(Ctx&: getASTContext(), CommonInfo: ACI);
2215 break;
2216
2217 case ParsedAttr::AT_HLSLRawBuffer:
2218 A = HLSLRawBufferAttr::Create(Ctx&: getASTContext(), CommonInfo: ACI);
2219 break;
2220
2221 case ParsedAttr::AT_HLSLIsCounter:
2222 A = HLSLIsCounterAttr::Create(Ctx&: getASTContext(), CommonInfo: ACI);
2223 break;
2224
2225 case ParsedAttr::AT_HLSLIsArray:
2226 A = HLSLIsArrayAttr::Create(Ctx&: getASTContext(), CommonInfo: ACI);
2227 break;
2228
2229 case ParsedAttr::AT_HLSLIsMultiSampled:
2230 A = HLSLIsMultiSampledAttr::Create(Ctx&: getASTContext(), CommonInfo: ACI);
2231 break;
2232
2233 case ParsedAttr::AT_HLSLContainedType: {
2234 if (AL.getNumArgs() != 1 && !AL.hasParsedType()) {
2235 Diag(Loc: AL.getLoc(), DiagID: diag::err_attribute_wrong_number_arguments) << AL << 1;
2236 return false;
2237 }
2238
2239 TypeSourceInfo *TSI = nullptr;
2240 QualType QT = SemaRef.GetTypeFromParser(Ty: AL.getTypeArg(), TInfo: &TSI);
2241 assert(TSI && "no type source info for attribute argument");
2242 if (SemaRef.RequireCompleteType(Loc: TSI->getTypeLoc().getBeginLoc(), T: QT,
2243 DiagID: diag::err_incomplete_type))
2244 return false;
2245 A = HLSLContainedTypeAttr::Create(Ctx&: getASTContext(), Type: TSI, CommonInfo: ACI);
2246 break;
2247 }
2248
2249 default:
2250 llvm_unreachable("unhandled HLSL attribute");
2251 }
2252
2253 HLSLResourcesTypeAttrs.emplace_back(Args&: A);
2254 return true;
2255}
2256
2257// Combines all resource type attributes and creates HLSLAttributedResourceType.
2258QualType SemaHLSL::ProcessResourceTypeAttributes(QualType CurrentType) {
2259 if (!HLSLResourcesTypeAttrs.size())
2260 return CurrentType;
2261
2262 QualType QT = CurrentType;
2263 HLSLAttributedResourceLocInfo LocInfo;
2264 if (CreateHLSLAttributedResourceType(S&: SemaRef, Wrapped: CurrentType,
2265 AttrList: HLSLResourcesTypeAttrs, ResType&: QT, LocInfo: &LocInfo)) {
2266 const HLSLAttributedResourceType *RT =
2267 cast<HLSLAttributedResourceType>(Val: QT.getTypePtr());
2268
2269 // Temporarily store TypeLoc information for the new type.
2270 // It will be transferred to HLSLAttributesResourceTypeLoc
2271 // shortly after the type is created by TypeSpecLocFiller which
2272 // will call the TakeLocForHLSLAttribute method below.
2273 LocsForHLSLAttributedResources.insert(KV: std::pair(RT, LocInfo));
2274 }
2275 HLSLResourcesTypeAttrs.clear();
2276 return QT;
2277}
2278
2279// Returns source location for the HLSLAttributedResourceType
2280HLSLAttributedResourceLocInfo
2281SemaHLSL::TakeLocForHLSLAttribute(const HLSLAttributedResourceType *RT) {
2282 HLSLAttributedResourceLocInfo LocInfo = {};
2283 auto I = LocsForHLSLAttributedResources.find(Val: RT);
2284 if (I != LocsForHLSLAttributedResources.end()) {
2285 LocInfo = I->second;
2286 LocsForHLSLAttributedResources.erase(I);
2287 return LocInfo;
2288 }
2289 LocInfo.Range = SourceRange();
2290 return LocInfo;
2291}
2292
2293// Walks though the global variable declaration, collects all resource binding
2294// requirements and adds them to Bindings
2295void SemaHLSL::collectResourceBindingsOnUserRecordDecl(const VarDecl *VD,
2296 const RecordType *RT) {
2297 const RecordDecl *RD = RT->getDecl()->getDefinitionOrSelf();
2298 for (FieldDecl *FD : RD->fields()) {
2299 const Type *Ty = FD->getType()->getUnqualifiedDesugaredType();
2300
2301 // Unwrap arrays
2302 // FIXME: Calculate array size while unwrapping
2303 assert(!Ty->isIncompleteArrayType() &&
2304 "incomplete arrays inside user defined types are not supported");
2305 while (Ty->isConstantArrayType()) {
2306 const ConstantArrayType *CAT = cast<ConstantArrayType>(Val: Ty);
2307 Ty = CAT->getElementType()->getUnqualifiedDesugaredType();
2308 }
2309
2310 if (!Ty->isRecordType())
2311 continue;
2312
2313 if (const HLSLAttributedResourceType *AttrResType =
2314 HLSLAttributedResourceType::findHandleTypeOnResource(RT: Ty)) {
2315 // Add a new DeclBindingInfo to Bindings if it does not already exist
2316 ResourceClass RC = AttrResType->getAttrs().ResourceClass;
2317 DeclBindingInfo *DBI = Bindings.getDeclBindingInfo(VD, ResClass: RC);
2318 if (!DBI)
2319 Bindings.addDeclBindingInfo(VD, ResClass: RC);
2320 } else if (const RecordType *RT = dyn_cast<RecordType>(Val: Ty)) {
2321 // Recursively scan embedded struct or class; it would be nice to do this
2322 // without recursion, but tricky to correctly calculate the size of the
2323 // binding, which is something we are probably going to need to do later
2324 // on. Hopefully nesting of structs in structs too many levels is
2325 // unlikely.
2326 collectResourceBindingsOnUserRecordDecl(VD, RT);
2327 }
2328 }
2329}
2330
2331// Diagnose localized register binding errors for a single binding; does not
2332// diagnose resource binding on user record types, that will be done later
2333// in processResourceBindingOnDecl based on the information collected in
2334// collectResourceBindingsOnVarDecl.
2335// Returns false if the register binding is not valid.
2336static bool DiagnoseLocalRegisterBinding(Sema &S, SourceLocation &ArgLoc,
2337 Decl *D, RegisterType RegType,
2338 bool SpecifiedSpace) {
2339 int RegTypeNum = static_cast<int>(RegType);
2340
2341 // check if the decl type is groupshared
2342 if (D->hasAttr<HLSLGroupSharedAddressSpaceAttr>()) {
2343 S.Diag(Loc: ArgLoc, DiagID: diag::err_hlsl_binding_type_mismatch) << RegTypeNum;
2344 return false;
2345 }
2346
2347 // Cbuffers and Tbuffers are HLSLBufferDecl types
2348 if (HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(Val: D)) {
2349 ResourceClass RC = CBufferOrTBuffer->isCBuffer() ? ResourceClass::CBuffer
2350 : ResourceClass::SRV;
2351 if (RegType == getRegisterType(RC))
2352 return true;
2353
2354 S.Diag(Loc: D->getLocation(), DiagID: diag::err_hlsl_binding_type_mismatch)
2355 << RegTypeNum;
2356 return false;
2357 }
2358
2359 // Samplers, UAVs, and SRVs are VarDecl types
2360 assert(isa<VarDecl>(D) && "D is expected to be VarDecl or HLSLBufferDecl");
2361 VarDecl *VD = cast<VarDecl>(Val: D);
2362
2363 // Resource
2364 if (const HLSLAttributedResourceType *AttrResType =
2365 HLSLAttributedResourceType::findHandleTypeOnResource(
2366 RT: VD->getType().getTypePtr())) {
2367 if (RegType == getRegisterType(ResTy: AttrResType))
2368 return true;
2369
2370 S.Diag(Loc: D->getLocation(), DiagID: diag::err_hlsl_binding_type_mismatch)
2371 << RegTypeNum;
2372 return false;
2373 }
2374
2375 const clang::Type *Ty = VD->getType().getTypePtr();
2376 while (Ty->isArrayType())
2377 Ty = Ty->getArrayElementTypeNoTypeQual();
2378
2379 // Basic types
2380 if (Ty->isArithmeticType() || Ty->isVectorType()) {
2381 bool DeclaredInCOrTBuffer = isa<HLSLBufferDecl>(Val: D->getDeclContext());
2382 if (SpecifiedSpace && !DeclaredInCOrTBuffer)
2383 S.Diag(Loc: ArgLoc, DiagID: diag::err_hlsl_space_on_global_constant);
2384
2385 if (!DeclaredInCOrTBuffer && (Ty->isIntegralType(Ctx: S.getASTContext()) ||
2386 Ty->isFloatingType() || Ty->isVectorType())) {
2387 // Register annotation on default constant buffer declaration ($Globals)
2388 if (RegType == RegisterType::CBuffer)
2389 S.Diag(Loc: ArgLoc, DiagID: diag::warn_hlsl_deprecated_register_type_b);
2390 else if (RegType != RegisterType::C)
2391 S.Diag(Loc: ArgLoc, DiagID: diag::err_hlsl_binding_type_mismatch) << RegTypeNum;
2392 else
2393 return true;
2394 } else {
2395 if (RegType == RegisterType::C)
2396 S.Diag(Loc: ArgLoc, DiagID: diag::warn_hlsl_register_type_c_packoffset);
2397 else
2398 S.Diag(Loc: ArgLoc, DiagID: diag::err_hlsl_binding_type_mismatch) << RegTypeNum;
2399 }
2400 return false;
2401 }
2402 if (Ty->isRecordType())
2403 // RecordTypes will be diagnosed in processResourceBindingOnDecl
2404 // that is called from ActOnVariableDeclarator
2405 return true;
2406
2407 // Anything else is an error
2408 S.Diag(Loc: ArgLoc, DiagID: diag::err_hlsl_binding_type_mismatch) << RegTypeNum;
2409 return false;
2410}
2411
2412static bool ValidateMultipleRegisterAnnotations(Sema &S, Decl *TheDecl,
2413 RegisterType regType) {
2414 // make sure that there are no two register annotations
2415 // applied to the decl with the same register type
2416 bool RegisterTypesDetected[5] = {false};
2417 RegisterTypesDetected[static_cast<int>(regType)] = true;
2418
2419 for (auto it = TheDecl->attr_begin(); it != TheDecl->attr_end(); ++it) {
2420 if (HLSLResourceBindingAttr *attr =
2421 dyn_cast<HLSLResourceBindingAttr>(Val: *it)) {
2422
2423 RegisterType otherRegType = attr->getRegisterType();
2424 if (RegisterTypesDetected[static_cast<int>(otherRegType)]) {
2425 int otherRegTypeNum = static_cast<int>(otherRegType);
2426 S.Diag(Loc: TheDecl->getLocation(),
2427 DiagID: diag::err_hlsl_duplicate_register_annotation)
2428 << otherRegTypeNum;
2429 return false;
2430 }
2431 RegisterTypesDetected[static_cast<int>(otherRegType)] = true;
2432 }
2433 }
2434 return true;
2435}
2436
2437static bool DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
2438 Decl *D, RegisterType RegType,
2439 bool SpecifiedSpace) {
2440
2441 // exactly one of these two types should be set
2442 assert(((isa<VarDecl>(D) && !isa<HLSLBufferDecl>(D)) ||
2443 (!isa<VarDecl>(D) && isa<HLSLBufferDecl>(D))) &&
2444 "expecting VarDecl or HLSLBufferDecl");
2445
2446 // check if the declaration contains resource matching the register type
2447 if (!DiagnoseLocalRegisterBinding(S, ArgLoc, D, RegType, SpecifiedSpace))
2448 return false;
2449
2450 // next, if multiple register annotations exist, check that none conflict.
2451 return ValidateMultipleRegisterAnnotations(S, TheDecl: D, regType: RegType);
2452}
2453
2454// return false if the slot count exceeds the limit, true otherwise
2455static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &StartSlot,
2456 const uint64_t &Limit,
2457 const ResourceClass ResClass,
2458 ASTContext &Ctx,
2459 uint64_t ArrayCount = 1) {
2460 Ty = Ty.getCanonicalType();
2461 const Type *T = Ty.getTypePtr();
2462
2463 // Early exit if already overflowed
2464 if (StartSlot > Limit)
2465 return false;
2466
2467 // Case 1: array type
2468 if (const auto *AT = dyn_cast<ArrayType>(Val: T)) {
2469 uint64_t Count = 1;
2470
2471 if (const auto *CAT = dyn_cast<ConstantArrayType>(Val: AT))
2472 Count = CAT->getSize().getZExtValue();
2473
2474 QualType ElemTy = AT->getElementType();
2475 return AccumulateHLSLResourceSlots(Ty: ElemTy, StartSlot, Limit, ResClass, Ctx,
2476 ArrayCount: ArrayCount * Count);
2477 }
2478
2479 // Case 2: resource leaf
2480 if (auto ResTy = dyn_cast<HLSLAttributedResourceType>(Val: T)) {
2481 // First ensure this resource counts towards the corresponding
2482 // register type limit.
2483 if (ResTy->getAttrs().ResourceClass != ResClass)
2484 return true;
2485
2486 // Validate highest slot used
2487 uint64_t EndSlot = StartSlot + ArrayCount - 1;
2488 if (EndSlot > Limit)
2489 return false;
2490
2491 // Advance SlotCount past the consumed range
2492 StartSlot = EndSlot + 1;
2493 return true;
2494 }
2495
2496 // Case 3: struct / record
2497 if (const auto *RT = dyn_cast<RecordType>(Val: T)) {
2498 const RecordDecl *RD = RT->getDecl();
2499
2500 if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(Val: RD)) {
2501 for (const CXXBaseSpecifier &Base : CXXRD->bases()) {
2502 if (!AccumulateHLSLResourceSlots(Ty: Base.getType(), StartSlot, Limit,
2503 ResClass, Ctx, ArrayCount))
2504 return false;
2505 }
2506 }
2507
2508 for (const FieldDecl *Field : RD->fields()) {
2509 if (!AccumulateHLSLResourceSlots(Ty: Field->getType(), StartSlot, Limit,
2510 ResClass, Ctx, ArrayCount))
2511 return false;
2512 }
2513
2514 return true;
2515 }
2516
2517 // Case 4: everything else
2518 return true;
2519}
2520
2521// return true if there is something invalid, false otherwise
2522static bool ValidateRegisterNumber(uint64_t SlotNum, Decl *TheDecl,
2523 ASTContext &Ctx, RegisterType RegTy) {
2524 const uint64_t Limit = UINT32_MAX;
2525 if (SlotNum > Limit)
2526 return true;
2527
2528 // after verifying the number doesn't exceed uint32max, we don't need
2529 // to look further into c or i register types
2530 if (RegTy == RegisterType::C || RegTy == RegisterType::I)
2531 return false;
2532
2533 if (VarDecl *VD = dyn_cast<VarDecl>(Val: TheDecl)) {
2534 uint64_t BaseSlot = SlotNum;
2535
2536 if (!AccumulateHLSLResourceSlots(Ty: VD->getType(), StartSlot&: SlotNum, Limit,
2537 ResClass: getResourceClass(RT: RegTy), Ctx))
2538 return true;
2539
2540 // After AccumulateHLSLResourceSlots runs, SlotNum is now
2541 // the first free slot; last used was SlotNum - 1
2542 return (BaseSlot > Limit);
2543 }
2544 // handle the cbuffer/tbuffer case
2545 if (isa<HLSLBufferDecl>(Val: TheDecl))
2546 // resources cannot be put within a cbuffer, so no need
2547 // to analyze the structure since the register number
2548 // won't be pushed any higher.
2549 return (SlotNum > Limit);
2550
2551 // we don't expect any other decl type, so fail
2552 llvm_unreachable("unexpected decl type");
2553}
2554
2555void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
2556 if (VarDecl *VD = dyn_cast<VarDecl>(Val: TheDecl)) {
2557 QualType Ty = VD->getType();
2558 if (const auto *IAT = dyn_cast<IncompleteArrayType>(Val&: Ty))
2559 Ty = IAT->getElementType();
2560 if (SemaRef.RequireCompleteType(Loc: TheDecl->getBeginLoc(), T: Ty,
2561 DiagID: diag::err_incomplete_type))
2562 return;
2563 }
2564
2565 StringRef Slot = "";
2566 StringRef Space = "";
2567 SourceLocation SlotLoc, SpaceLoc;
2568
2569 if (!AL.isArgIdent(Arg: 0)) {
2570 Diag(Loc: AL.getLoc(), DiagID: diag::err_attribute_argument_type)
2571 << AL << AANT_ArgumentIdentifier;
2572 return;
2573 }
2574 IdentifierLoc *Loc = AL.getArgAsIdent(Arg: 0);
2575
2576 if (AL.getNumArgs() == 2) {
2577 Slot = Loc->getIdentifierInfo()->getName();
2578 SlotLoc = Loc->getLoc();
2579 if (!AL.isArgIdent(Arg: 1)) {
2580 Diag(Loc: AL.getLoc(), DiagID: diag::err_attribute_argument_type)
2581 << AL << AANT_ArgumentIdentifier;
2582 return;
2583 }
2584 Loc = AL.getArgAsIdent(Arg: 1);
2585 Space = Loc->getIdentifierInfo()->getName();
2586 SpaceLoc = Loc->getLoc();
2587 } else {
2588 StringRef Str = Loc->getIdentifierInfo()->getName();
2589 if (Str.starts_with(Prefix: "space")) {
2590 Space = Str;
2591 SpaceLoc = Loc->getLoc();
2592 } else {
2593 Slot = Str;
2594 SlotLoc = Loc->getLoc();
2595 Space = "space0";
2596 }
2597 }
2598
2599 RegisterType RegType = RegisterType::SRV;
2600 std::optional<unsigned> SlotNum;
2601 unsigned SpaceNum = 0;
2602
2603 // Validate slot
2604 if (!Slot.empty()) {
2605 if (!convertToRegisterType(Slot, RT: &RegType)) {
2606 Diag(Loc: SlotLoc, DiagID: diag::err_hlsl_binding_type_invalid) << Slot.substr(Start: 0, N: 1);
2607 return;
2608 }
2609 if (RegType == RegisterType::I) {
2610 Diag(Loc: SlotLoc, DiagID: diag::warn_hlsl_deprecated_register_type_i);
2611 return;
2612 }
2613 const StringRef SlotNumStr = Slot.substr(Start: 1);
2614
2615 uint64_t N;
2616
2617 // validate that the slot number is a non-empty number
2618 if (SlotNumStr.getAsInteger(Radix: 10, Result&: N)) {
2619 Diag(Loc: SlotLoc, DiagID: diag::err_hlsl_unsupported_register_number);
2620 return;
2621 }
2622
2623 // Validate register number. It should not exceed UINT32_MAX,
2624 // including if the resource type is an array that starts
2625 // before UINT32_MAX, but ends afterwards.
2626 if (ValidateRegisterNumber(SlotNum: N, TheDecl, Ctx&: getASTContext(), RegTy: RegType)) {
2627 Diag(Loc: SlotLoc, DiagID: diag::err_hlsl_register_number_too_large);
2628 return;
2629 }
2630
2631 // the slot number has been validated and does not exceed UINT32_MAX
2632 SlotNum = (unsigned)N;
2633 }
2634
2635 // Validate space
2636 if (!Space.starts_with(Prefix: "space")) {
2637 Diag(Loc: SpaceLoc, DiagID: diag::err_hlsl_expected_space) << Space;
2638 return;
2639 }
2640 StringRef SpaceNumStr = Space.substr(Start: 5);
2641 if (SpaceNumStr.getAsInteger(Radix: 10, Result&: SpaceNum)) {
2642 Diag(Loc: SpaceLoc, DiagID: diag::err_hlsl_expected_space) << Space;
2643 return;
2644 }
2645
2646 // If we have slot, diagnose it is the right register type for the decl
2647 if (SlotNum.has_value())
2648 if (!DiagnoseHLSLRegisterAttribute(S&: SemaRef, ArgLoc&: SlotLoc, D: TheDecl, RegType,
2649 SpecifiedSpace: !SpaceLoc.isInvalid()))
2650 return;
2651
2652 HLSLResourceBindingAttr *NewAttr =
2653 HLSLResourceBindingAttr::Create(Ctx&: getASTContext(), Slot, Space, CommonInfo: AL);
2654 if (NewAttr) {
2655 NewAttr->setBinding(RT: RegType, SlotNum, SpaceNum);
2656 TheDecl->addAttr(A: NewAttr);
2657 }
2658}
2659
2660void SemaHLSL::handleParamModifierAttr(Decl *D, const ParsedAttr &AL) {
2661 HLSLParamModifierAttr *NewAttr = mergeParamModifierAttr(
2662 D, AL,
2663 Spelling: static_cast<HLSLParamModifierAttr::Spelling>(AL.getSemanticSpelling()));
2664 if (NewAttr)
2665 D->addAttr(A: NewAttr);
2666}
2667
2668static bool isMatrixOrArrayOfMatrix(const ASTContext &Ctx, QualType QT) {
2669 const Type *Ty = QT->getUnqualifiedDesugaredType();
2670 while (isa<ArrayType>(Val: Ty))
2671 Ty = Ty->getArrayElementTypeNoTypeQual();
2672 return Ty->isDependentType() || Ty->isConstantMatrixType();
2673}
2674
2675/// Walks the existing AttributedType sugar of \p T looking for a previously
2676/// applied HLSLRowMajor/HLSLColumnMajor marker. If one is found, populates
2677/// \p ExistingKind with its attr::Kind and returns true.
2678static bool findExistingMatrixLayoutMarker(QualType T,
2679 attr::Kind &ExistingKind) {
2680 QualType Cur = T;
2681 while (const auto *AT = Cur->getAs<AttributedType>()) {
2682 attr::Kind K = AT->getAttrKind();
2683 if (K == attr::HLSLRowMajor || K == attr::HLSLColumnMajor) {
2684 ExistingKind = K;
2685 return true;
2686 }
2687 Cur = AT->getModifiedType();
2688 }
2689 return false;
2690}
2691
2692Attr *SemaHLSL::buildMatrixLayoutTypeAttr(QualType T, const ParsedAttr &AL) {
2693 if (T.isNull())
2694 return nullptr;
2695
2696 ASTContext &Ctx = getASTContext();
2697 attr::Kind AttrK = AL.getKind() == ParsedAttr::AT_HLSLRowMajor
2698 ? attr::HLSLRowMajor
2699 : attr::HLSLColumnMajor;
2700
2701 // For non-dependent types, the operand must be a matrix (or array of
2702 // matrices).
2703 if (!T->isDependentType() && !isMatrixOrArrayOfMatrix(Ctx, QT: T)) {
2704 Diag(Loc: AL.getLoc(), DiagID: diag::err_hlsl_matrix_layout_non_matrix)
2705 << AL.getAttrName();
2706 AL.setInvalid();
2707 return nullptr;
2708 }
2709
2710 // Conflict / duplicate detection by walking existing sugar.
2711 attr::Kind ExistingKind;
2712 if (findExistingMatrixLayoutMarker(T, ExistingKind)) {
2713 if (ExistingKind == AttrK) {
2714 Diag(Loc: AL.getLoc(), DiagID: diag::warn_duplicate_attribute_exact)
2715 << AL.getAttrName();
2716 Diag(Loc: AL.getLoc(), DiagID: diag::note_previous_attribute);
2717 return nullptr;
2718 }
2719 IdentifierInfo *ExistingII = &Ctx.Idents.get(
2720 Name: ExistingKind == attr::HLSLRowMajor ? "row_major" : "column_major");
2721 Diag(Loc: AL.getLoc(), DiagID: diag::err_hlsl_matrix_layout_conflict)
2722 << AL.getAttrName() << ExistingII;
2723 Diag(Loc: AL.getLoc(), DiagID: diag::note_conflicting_attribute);
2724 AL.setInvalid();
2725 return nullptr;
2726 }
2727
2728 if (AttrK == attr::HLSLRowMajor)
2729 return ::new (Ctx) HLSLRowMajorAttr(Ctx, AL);
2730 return ::new (Ctx) HLSLColumnMajorAttr(Ctx, AL);
2731}
2732
2733// Re-validates an HLSL `row_major` / `column_major` attribute after template
2734// substitution. The parse-time check in `buildMatrixLayoutTypeAttr` is skipped
2735// for dependent types; `TransformAttributedType` calls this once the type is
2736// concrete. Returns `true` (and emits a diagnostic) if the substituted type is
2737// not a matrix or array of matrices, signaling the caller to abort the
2738// transform.
2739bool SemaHLSL::diagnoseMatrixLayoutInstantiation(attr::Kind K, QualType T,
2740 SourceLocation Loc) {
2741 if (K != attr::HLSLRowMajor && K != attr::HLSLColumnMajor)
2742 return false;
2743 if (T.isNull() || T->isDependentType())
2744 return false;
2745 if (isMatrixOrArrayOfMatrix(Ctx: getASTContext(), QT: T))
2746 return false;
2747 IdentifierInfo *II = &getASTContext().Idents.get(
2748 Name: K == attr::HLSLRowMajor ? "row_major" : "column_major");
2749 Diag(Loc, DiagID: diag::err_hlsl_matrix_layout_non_matrix) << II;
2750 return true;
2751}
2752
2753// Transpose and matrix mul need to read the destination layout.
2754// Elementwise builtins reuse the operand layout instead.
2755static bool isLayoutAdaptingMatrixBuiltin(unsigned BuiltinID) {
2756 switch (BuiltinID) {
2757 case Builtin::BI__builtin_hlsl_mul:
2758 case Builtin::BI__builtin_hlsl_transpose:
2759 return true;
2760 default:
2761 return false;
2762 }
2763}
2764
2765void SemaHLSL::propagateContextualMatrixLayout(Expr *E, QualType DestType) {
2766 if (!E || DestType.isNull())
2767 return;
2768 const auto *DestMat = DestType->getAs<ConstantMatrixType>();
2769 if (!DestMat)
2770 return;
2771 auto *Call = dyn_cast<CallExpr>(Val: E->IgnoreParenImpCasts());
2772 if (!Call)
2773 return;
2774 const FunctionDecl *Callee = Call->getDirectCallee();
2775 if (!Callee || !isLayoutAdaptingMatrixBuiltin(BuiltinID: Callee->getBuiltinID()))
2776 return;
2777 const auto *CallMat = Call->getType()->getAs<ConstantMatrixType>();
2778 if (!CallMat || CallMat->getNumRows() != DestMat->getNumRows() ||
2779 CallMat->getNumColumns() != DestMat->getNumColumns())
2780 return;
2781 // Re-type the call with the destination sugar so CodeGen lowers into that
2782 // layout, not the TU default.
2783 Call->setType(DestType.getUnqualifiedType());
2784}
2785
2786namespace {
2787
2788/// This class implements HLSL availability diagnostics for default
2789/// and relaxed mode
2790///
2791/// The goal of this diagnostic is to emit an error or warning when an
2792/// unavailable API is found in code that is reachable from the shader
2793/// entry function or from an exported function (when compiling a shader
2794/// library).
2795///
2796/// This is done by traversing the AST of all shader entry point functions
2797/// and of all exported functions, and any functions that are referenced
2798/// from this AST. In other words, any functions that are reachable from
2799/// the entry points.
2800class DiagnoseHLSLAvailability : public DynamicRecursiveASTVisitor {
2801 Sema &SemaRef;
2802
2803 // Stack of functions to be scaned
2804 llvm::SmallVector<const FunctionDecl *, 8> DeclsToScan;
2805
2806 // Tracks which environments functions have been scanned in.
2807 //
2808 // Maps FunctionDecl to an unsigned number that represents the set of shader
2809 // environments the function has been scanned for.
2810 // The llvm::Triple::EnvironmentType enum values for shader stages guaranteed
2811 // to be numbered from llvm::Triple::Pixel to llvm::Triple::Amplification
2812 // (verified by static_asserts in Triple.cpp), we can use it to index
2813 // individual bits in the set, as long as we shift the values to start with 0
2814 // by subtracting the value of llvm::Triple::Pixel first.
2815 //
2816 // The N'th bit in the set will be set if the function has been scanned
2817 // in shader environment whose llvm::Triple::EnvironmentType integer value
2818 // equals (llvm::Triple::Pixel + N).
2819 //
2820 // For example, if a function has been scanned in compute and pixel stage
2821 // environment, the value will be 0x21 (100001 binary) because:
2822 //
2823 // (int)(llvm::Triple::Pixel - llvm::Triple::Pixel) == 0
2824 // (int)(llvm::Triple::Compute - llvm::Triple::Pixel) == 5
2825 //
2826 // A FunctionDecl is mapped to 0 (or not included in the map) if it has not
2827 // been scanned in any environment.
2828 llvm::DenseMap<const FunctionDecl *, unsigned> ScannedDecls;
2829
2830 // Do not access these directly, use the get/set methods below to make
2831 // sure the values are in sync
2832 llvm::Triple::EnvironmentType CurrentShaderEnvironment;
2833 unsigned CurrentShaderStageBit;
2834
2835 // True if scanning a function that was already scanned in a different
2836 // shader stage context, and therefore we should not report issues that
2837 // depend only on shader model version because they would be duplicate.
2838 bool ReportOnlyShaderStageIssues;
2839
2840 // Helper methods for dealing with current stage context / environment
2841 void SetShaderStageContext(llvm::Triple::EnvironmentType ShaderType) {
2842 static_assert(sizeof(unsigned) >= 4);
2843 assert(HLSLShaderAttr::isValidShaderType(ShaderType));
2844 assert((unsigned)(ShaderType - llvm::Triple::Pixel) < 31 &&
2845 "ShaderType is too big for this bitmap"); // 31 is reserved for
2846 // "unknown"
2847
2848 unsigned bitmapIndex = ShaderType - llvm::Triple::Pixel;
2849 CurrentShaderEnvironment = ShaderType;
2850 CurrentShaderStageBit = (1 << bitmapIndex);
2851 }
2852
2853 void SetUnknownShaderStageContext() {
2854 CurrentShaderEnvironment = llvm::Triple::UnknownEnvironment;
2855 CurrentShaderStageBit = (1 << 31);
2856 }
2857
2858 llvm::Triple::EnvironmentType GetCurrentShaderEnvironment() const {
2859 return CurrentShaderEnvironment;
2860 }
2861
2862 bool InUnknownShaderStageContext() const {
2863 return CurrentShaderEnvironment == llvm::Triple::UnknownEnvironment;
2864 }
2865
2866 // Helper methods for dealing with shader stage bitmap
2867 void AddToScannedFunctions(const FunctionDecl *FD) {
2868 unsigned &ScannedStages = ScannedDecls[FD];
2869 ScannedStages |= CurrentShaderStageBit;
2870 }
2871
2872 unsigned GetScannedStages(const FunctionDecl *FD) { return ScannedDecls[FD]; }
2873
2874 bool WasAlreadyScannedInCurrentStage(const FunctionDecl *FD) {
2875 return WasAlreadyScannedInCurrentStage(ScannerStages: GetScannedStages(FD));
2876 }
2877
2878 bool WasAlreadyScannedInCurrentStage(unsigned ScannerStages) {
2879 return ScannerStages & CurrentShaderStageBit;
2880 }
2881
2882 static bool NeverBeenScanned(unsigned ScannedStages) {
2883 return ScannedStages == 0;
2884 }
2885
2886 // Scanning methods
2887 void HandleFunctionOrMethodRef(FunctionDecl *FD, Expr *RefExpr);
2888 void CheckDeclAvailability(NamedDecl *D, const AvailabilityAttr *AA,
2889 SourceRange Range);
2890 const AvailabilityAttr *FindAvailabilityAttr(const Decl *D);
2891 bool HasMatchingEnvironmentOrNone(const AvailabilityAttr *AA);
2892
2893public:
2894 DiagnoseHLSLAvailability(Sema &SemaRef)
2895 : SemaRef(SemaRef),
2896 CurrentShaderEnvironment(llvm::Triple::UnknownEnvironment),
2897 CurrentShaderStageBit(0), ReportOnlyShaderStageIssues(false) {}
2898
2899 // AST traversal methods
2900 void RunOnTranslationUnit(const TranslationUnitDecl *TU);
2901 void RunOnFunction(const FunctionDecl *FD);
2902
2903 bool VisitDeclRefExpr(DeclRefExpr *DRE) override {
2904 FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(Val: DRE->getDecl());
2905 if (FD)
2906 HandleFunctionOrMethodRef(FD, RefExpr: DRE);
2907 return true;
2908 }
2909
2910 bool VisitMemberExpr(MemberExpr *ME) override {
2911 FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(Val: ME->getMemberDecl());
2912 if (FD)
2913 HandleFunctionOrMethodRef(FD, RefExpr: ME);
2914 return true;
2915 }
2916};
2917
2918void DiagnoseHLSLAvailability::HandleFunctionOrMethodRef(FunctionDecl *FD,
2919 Expr *RefExpr) {
2920 assert((isa<DeclRefExpr>(RefExpr) || isa<MemberExpr>(RefExpr)) &&
2921 "expected DeclRefExpr or MemberExpr");
2922
2923 if (const AvailabilityAttr *AA = FindAvailabilityAttr(D: FD))
2924 CheckDeclAvailability(
2925 D: FD, AA, Range: SourceRange(RefExpr->getBeginLoc(), RefExpr->getEndLoc()));
2926
2927 // has a definition -> add to stack to be scanned
2928 const FunctionDecl *FDWithBody = nullptr;
2929 if (FD->hasBody(Definition&: FDWithBody) && !WasAlreadyScannedInCurrentStage(FD: FDWithBody))
2930 DeclsToScan.push_back(Elt: FDWithBody);
2931}
2932
2933void DiagnoseHLSLAvailability::RunOnTranslationUnit(
2934 const TranslationUnitDecl *TU) {
2935 const TargetInfo &TargetInfo = SemaRef.getASTContext().getTargetInfo();
2936 std::string &EntryName = TargetInfo.getTargetOpts().HLSLEntry;
2937 bool IsLibraryShader = TargetInfo.getTriple().getEnvironment() ==
2938 llvm::Triple::EnvironmentType::Library;
2939 SourceLocation EntryLoc{};
2940
2941 // Iterate over all shader entry functions and library exports, and for those
2942 // that have a body (definiton), run diag scan on each, setting appropriate
2943 // shader environment context based on whether it is a shader entry function
2944 // or an exported function. Exported functions can be in namespaces and in
2945 // export declarations so we need to scan those declaration contexts as well.
2946 llvm::SmallVector<const DeclContext *, 8> DeclContextsToScan;
2947 DeclContextsToScan.push_back(Elt: TU);
2948
2949 while (!DeclContextsToScan.empty()) {
2950 const DeclContext *DC = DeclContextsToScan.pop_back_val();
2951 for (auto &D : DC->decls()) {
2952 // do not scan implicit declaration generated by the implementation
2953 if (D->isImplicit())
2954 continue;
2955
2956 // for namespace or export declaration add the context to the list to be
2957 // scanned later
2958 if (llvm::dyn_cast<NamespaceDecl>(Val: D) || llvm::dyn_cast<ExportDecl>(Val: D)) {
2959 DeclContextsToScan.push_back(Elt: llvm::dyn_cast<DeclContext>(Val: D));
2960 continue;
2961 }
2962
2963 // skip over other decls or function decls without body
2964 const FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(Val: D);
2965 if (!FD || !FD->isThisDeclarationADefinition())
2966 continue;
2967
2968 // shader entry point
2969 if (HLSLShaderAttr *ShaderAttr = FD->getAttr<HLSLShaderAttr>()) {
2970 if (!IsLibraryShader && FD->getName() == EntryName) {
2971 if (EntryLoc.isValid()) {
2972 SemaRef.Diag(Loc: FD->getLocation(),
2973 DiagID: diag::err_hlsl_ambiguous_entry_point)
2974 << EntryName;
2975 SemaRef.Diag(Loc: EntryLoc, DiagID: diag::note_previous_declaration_as)
2976 << EntryName;
2977 return;
2978 }
2979 EntryLoc = FD->getLocation();
2980 }
2981 SetShaderStageContext(ShaderAttr->getType());
2982 RunOnFunction(FD);
2983 continue;
2984 }
2985 // exported library function
2986 // FIXME: replace this loop with external linkage check once issue #92071
2987 // is resolved
2988 bool isExport = FD->isInExportDeclContext();
2989 if (!isExport) {
2990 for (const auto *Redecl : FD->redecls()) {
2991 if (Redecl->isInExportDeclContext()) {
2992 isExport = true;
2993 break;
2994 }
2995 }
2996 }
2997 if (isExport) {
2998 SetUnknownShaderStageContext();
2999 RunOnFunction(FD);
3000 continue;
3001 }
3002 }
3003 }
3004
3005 if (!IsLibraryShader && EntryLoc.isInvalid()) {
3006 SemaRef.Diag(Loc: TU->getLocation(), DiagID: diag::err_hlsl_missing_entry_point)
3007 << EntryName;
3008 return;
3009 }
3010}
3011
3012void DiagnoseHLSLAvailability::RunOnFunction(const FunctionDecl *FD) {
3013 assert(DeclsToScan.empty() && "DeclsToScan should be empty");
3014 DeclsToScan.push_back(Elt: FD);
3015
3016 while (!DeclsToScan.empty()) {
3017 // Take one decl from the stack and check it by traversing its AST.
3018 // For any CallExpr found during the traversal add it's callee to the top of
3019 // the stack to be processed next. Functions already processed are stored in
3020 // ScannedDecls.
3021 const FunctionDecl *FD = DeclsToScan.pop_back_val();
3022
3023 // Decl was already scanned
3024 const unsigned ScannedStages = GetScannedStages(FD);
3025 if (WasAlreadyScannedInCurrentStage(ScannerStages: ScannedStages))
3026 continue;
3027
3028 ReportOnlyShaderStageIssues = !NeverBeenScanned(ScannedStages);
3029
3030 AddToScannedFunctions(FD);
3031 TraverseStmt(S: FD->getBody());
3032 }
3033}
3034
3035bool DiagnoseHLSLAvailability::HasMatchingEnvironmentOrNone(
3036 const AvailabilityAttr *AA) {
3037 const IdentifierInfo *IIEnvironment = AA->getEnvironment();
3038 if (!IIEnvironment)
3039 return true;
3040
3041 llvm::Triple::EnvironmentType CurrentEnv = GetCurrentShaderEnvironment();
3042 if (CurrentEnv == llvm::Triple::UnknownEnvironment)
3043 return false;
3044
3045 llvm::Triple::EnvironmentType AttrEnv =
3046 AvailabilityAttr::getEnvironmentType(Environment: IIEnvironment->getName());
3047
3048 return CurrentEnv == AttrEnv;
3049}
3050
3051const AvailabilityAttr *
3052DiagnoseHLSLAvailability::FindAvailabilityAttr(const Decl *D) {
3053 AvailabilityAttr const *PartialMatch = nullptr;
3054 // Check each AvailabilityAttr to find the one for this platform.
3055 // For multiple attributes with the same platform try to find one for this
3056 // environment.
3057 for (const auto *A : D->attrs()) {
3058 if (const auto *Avail = dyn_cast<AvailabilityAttr>(Val: A)) {
3059 const AvailabilityAttr *EffectiveAvail = Avail->getEffectiveAttr();
3060 StringRef AttrPlatform = EffectiveAvail->getPlatform()->getName();
3061 StringRef TargetPlatform =
3062 SemaRef.getASTContext().getTargetInfo().getPlatformName();
3063
3064 // Match the platform name.
3065 if (AttrPlatform == TargetPlatform) {
3066 // Find the best matching attribute for this environment
3067 if (HasMatchingEnvironmentOrNone(AA: EffectiveAvail))
3068 return Avail;
3069 PartialMatch = Avail;
3070 }
3071 }
3072 }
3073 return PartialMatch;
3074}
3075
3076// Check availability against target shader model version and current shader
3077// stage and emit diagnostic
3078void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D,
3079 const AvailabilityAttr *AA,
3080 SourceRange Range) {
3081
3082 const IdentifierInfo *IIEnv = AA->getEnvironment();
3083
3084 if (!IIEnv) {
3085 // The availability attribute does not have environment -> it depends only
3086 // on shader model version and not on specific the shader stage.
3087
3088 // Skip emitting the diagnostics if the diagnostic mode is set to
3089 // strict (-fhlsl-strict-availability) because all relevant diagnostics
3090 // were already emitted in the DiagnoseUnguardedAvailability scan
3091 // (SemaAvailability.cpp).
3092 if (SemaRef.getLangOpts().HLSLStrictAvailability)
3093 return;
3094
3095 // Do not report shader-stage-independent issues if scanning a function
3096 // that was already scanned in a different shader stage context (they would
3097 // be duplicate)
3098 if (ReportOnlyShaderStageIssues)
3099 return;
3100
3101 } else {
3102 // The availability attribute has environment -> we need to know
3103 // the current stage context to property diagnose it.
3104 if (InUnknownShaderStageContext())
3105 return;
3106 }
3107
3108 // Check introduced version and if environment matches
3109 bool EnvironmentMatches = HasMatchingEnvironmentOrNone(AA);
3110 VersionTuple Introduced = AA->getIntroduced();
3111 VersionTuple TargetVersion =
3112 SemaRef.Context.getTargetInfo().getPlatformMinVersion();
3113
3114 if (TargetVersion >= Introduced && EnvironmentMatches)
3115 return;
3116
3117 // Emit diagnostic message
3118 const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
3119 llvm::StringRef PlatformName(
3120 AvailabilityAttr::getPrettyPlatformName(Platform: TI.getPlatformName()));
3121
3122 llvm::StringRef CurrentEnvStr =
3123 llvm::Triple::getEnvironmentTypeName(Kind: GetCurrentShaderEnvironment());
3124
3125 llvm::StringRef AttrEnvStr =
3126 AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
3127 bool UseEnvironment = !AttrEnvStr.empty();
3128
3129 if (EnvironmentMatches) {
3130 SemaRef.Diag(Loc: Range.getBegin(), DiagID: diag::warn_hlsl_availability)
3131 << Range << D << PlatformName << Introduced.getAsString()
3132 << UseEnvironment << CurrentEnvStr;
3133 } else {
3134 SemaRef.Diag(Loc: Range.getBegin(), DiagID: diag::warn_hlsl_availability_unavailable)
3135 << Range << D;
3136 }
3137
3138 SemaRef.Diag(Loc: D->getLocation(), DiagID: diag::note_partial_availability_specified_here)
3139 << D << PlatformName << Introduced.getAsString()
3140 << SemaRef.Context.getTargetInfo().getPlatformMinVersion().getAsString()
3141 << UseEnvironment << AttrEnvStr << CurrentEnvStr;
3142}
3143
3144} // namespace
3145
3146void SemaHLSL::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) {
3147 // process default CBuffer - create buffer layout struct and invoke codegenCGH
3148 if (!DefaultCBufferDecls.empty()) {
3149 HLSLBufferDecl *DefaultCBuffer = HLSLBufferDecl::CreateDefaultCBuffer(
3150 C&: SemaRef.getASTContext(), LexicalParent: SemaRef.getCurLexicalContext(),
3151 DefaultCBufferDecls);
3152 addImplicitBindingAttrToDecl(S&: SemaRef, D: DefaultCBuffer, RT: RegisterType::CBuffer,
3153 ImplicitBindingOrderID: getNextImplicitBindingOrderID());
3154 SemaRef.getCurLexicalContext()->addDecl(D: DefaultCBuffer);
3155 createHostLayoutStructForBuffer(S&: SemaRef, BufDecl: DefaultCBuffer);
3156
3157 // Set HasValidPackoffset if any of the decls has a register(c#) annotation;
3158 for (const Decl *VD : DefaultCBufferDecls) {
3159 const HLSLResourceBindingAttr *RBA =
3160 VD->getAttr<HLSLResourceBindingAttr>();
3161 if (RBA && RBA->hasRegisterSlot() &&
3162 RBA->getRegisterType() == HLSLResourceBindingAttr::RegisterType::C) {
3163 DefaultCBuffer->setHasValidPackoffset(true);
3164 break;
3165 }
3166 }
3167
3168 DeclGroupRef DG(DefaultCBuffer);
3169 SemaRef.Consumer.HandleTopLevelDecl(D: DG);
3170 }
3171 diagnoseAvailabilityViolations(TU);
3172}
3173
3174// For resource member access through a global struct array, verify that the
3175// array index selecting the struct element is a constant integer expression.
3176// Returns false if the member expression is invalid.
3177bool SemaHLSL::ActOnResourceMemberAccessExpr(MemberExpr *ME) {
3178 assert((ME->getType()->isHLSLResourceRecord() ||
3179 ME->getType()->isHLSLResourceRecordArray()) &&
3180 "expected member expr to have resource record type or array of them");
3181
3182 // Walk the AST from MemberExpr to the VarDecl of the parent struct instance
3183 // and take note of any non-constant array indexing along the way. If the
3184 // VarDecl we find is a global variable, report error if there was any
3185 // non-constant array index in the resource member access along the way.
3186 const Expr *NonConstIndexExpr = nullptr;
3187 const Expr *E = ME->getBase();
3188 while (E) {
3189 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Val: E)) {
3190 if (!NonConstIndexExpr)
3191 return true;
3192
3193 const VarDecl *VD = cast<VarDecl>(Val: DRE->getDecl());
3194 if (!VD->hasGlobalStorage())
3195 return true;
3196
3197 SemaRef.Diag(Loc: NonConstIndexExpr->getExprLoc(),
3198 DiagID: diag::err_hlsl_resource_member_array_access_not_constant);
3199 return false;
3200 }
3201
3202 if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(Val: E)) {
3203 const Expr *IdxExpr = ASE->getIdx();
3204 if (!IdxExpr->isIntegerConstantExpr(Ctx: SemaRef.getASTContext()))
3205 NonConstIndexExpr = IdxExpr;
3206 E = ASE->getBase();
3207 } else if (const auto *SubME = dyn_cast<MemberExpr>(Val: E)) {
3208 E = SubME->getBase();
3209 } else if (const auto *ICE = dyn_cast<ImplicitCastExpr>(Val: E)) {
3210 E = ICE->getSubExpr();
3211 } else {
3212 llvm_unreachable("unexpected expr type in resource member access");
3213 }
3214 }
3215 return true;
3216}
3217
3218NamedDecl *SemaHLSL::getConstantBufferConversionFunction(QualType Type,
3219 CXXRecordDecl *RD) {
3220 QualType AddrSpaceType =
3221 SemaRef.Context.getCanonicalType(T: SemaRef.Context.getAddrSpaceQualType(
3222 T: Type.withConst(), AddressSpace: LangAS::hlsl_constant));
3223 QualType ReturnTy = SemaRef.Context.getCanonicalType(
3224 T: SemaRef.Context.getLValueReferenceType(T: AddrSpaceType));
3225
3226 DeclarationName ConvName =
3227 SemaRef.Context.DeclarationNames.getCXXConversionFunctionName(
3228 Ty: CanQualType::CreateUnsafe(Other: ReturnTy));
3229 LookupResult ConvR(SemaRef, ConvName, SourceLocation(),
3230 Sema::LookupOrdinaryName);
3231 [[maybe_unused]] bool LookupSucceeded =
3232 SemaRef.LookupQualifiedName(R&: ConvR, LookupCtx: RD);
3233 assert(LookupSucceeded);
3234
3235 for (NamedDecl *D : ConvR) {
3236 if (isa<CXXConversionDecl>(Val: D->getUnderlyingDecl()))
3237 return D;
3238 }
3239 return nullptr;
3240}
3241
3242std::optional<ExprResult>
3243SemaHLSL::tryPerformConstantBufferConversion(Expr *BaseExpr) {
3244 QualType BaseType = BaseExpr->getType();
3245 const HLSLAttributedResourceType *ResTy =
3246 HLSLAttributedResourceType::findHandleTypeOnResource(
3247 RT: BaseType.getTypePtr());
3248 if (!ResTy ||
3249 ResTy->getAttrs().ResourceClass != llvm::dxil::ResourceClass::CBuffer)
3250 return std::nullopt;
3251
3252 QualType TemplateType = ResTy->getContainedType();
3253
3254 NamedDecl *NamedConversionDecl = getConstantBufferConversionFunction(
3255 Type: TemplateType, RD: BaseType->getAsCXXRecordDecl());
3256 assert(NamedConversionDecl &&
3257 "Could not find conversion function for ConstantBuffer.");
3258 auto *ConversionDecl =
3259 cast<CXXConversionDecl>(Val: NamedConversionDecl->getUnderlyingDecl());
3260
3261 return SemaRef.BuildCXXMemberCallExpr(Exp: BaseExpr, FoundDecl: NamedConversionDecl,
3262 Method: ConversionDecl,
3263 /*HadMultipleCandidates=*/false);
3264}
3265
3266void SemaHLSL::diagnoseAvailabilityViolations(TranslationUnitDecl *TU) {
3267 // Skip running the diagnostics scan if the diagnostic mode is
3268 // strict (-fhlsl-strict-availability) and the target shader stage is known
3269 // because all relevant diagnostics were already emitted in the
3270 // DiagnoseUnguardedAvailability scan (SemaAvailability.cpp).
3271 const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
3272 if (SemaRef.getLangOpts().HLSLStrictAvailability &&
3273 TI.getTriple().getEnvironment() != llvm::Triple::EnvironmentType::Library)
3274 return;
3275
3276 DiagnoseHLSLAvailability(SemaRef).RunOnTranslationUnit(TU);
3277}
3278
3279static bool CheckAllArgsHaveSameType(Sema *S, CallExpr *TheCall) {
3280 assert(TheCall->getNumArgs() > 1);
3281 QualType ArgTy0 = TheCall->getArg(Arg: 0)->getType();
3282
3283 for (unsigned I = 1, N = TheCall->getNumArgs(); I < N; ++I) {
3284 if (!S->getASTContext().hasSameUnqualifiedType(
3285 T1: ArgTy0, T2: TheCall->getArg(Arg: I)->getType())) {
3286 S->Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_vec_builtin_incompatible_vector)
3287 << TheCall->getDirectCallee() << /*useAllTerminology*/ true
3288 << SourceRange(TheCall->getArg(Arg: 0)->getBeginLoc(),
3289 TheCall->getArg(Arg: N - 1)->getEndLoc());
3290 return true;
3291 }
3292 }
3293 return false;
3294}
3295
3296static bool CheckArgTypeMatches(Sema *S, Expr *Arg, QualType ExpectedType) {
3297 QualType ArgType = Arg->getType();
3298 if (!S->getASTContext().hasSameUnqualifiedType(T1: ArgType, T2: ExpectedType)) {
3299 S->Diag(Loc: Arg->getBeginLoc(), DiagID: diag::err_typecheck_convert_incompatible)
3300 << ArgType << ExpectedType << 1 << 0 << 0;
3301 return true;
3302 }
3303 return false;
3304}
3305
3306static bool CheckAllArgTypesAreCorrect(
3307 Sema *S, CallExpr *TheCall,
3308 llvm::function_ref<bool(Sema *S, SourceLocation Loc, int ArgOrdinal,
3309 clang::QualType PassedType)>
3310 Check) {
3311 for (unsigned I = 0; I < TheCall->getNumArgs(); ++I) {
3312 Expr *Arg = TheCall->getArg(Arg: I);
3313 if (Check(S, Arg->getBeginLoc(), I + 1, Arg->getType()))
3314 return true;
3315 }
3316 return false;
3317}
3318
3319static bool CheckFloatRepresentation(Sema *S, SourceLocation Loc,
3320 int ArgOrdinal,
3321 clang::QualType PassedType) {
3322 clang::QualType BaseType =
3323 PassedType->isVectorType()
3324 ? PassedType->castAs<clang::VectorType>()->getElementType()
3325 : PassedType;
3326 if (!BaseType->isFloat32Type())
3327 return S->Diag(Loc, DiagID: diag::err_builtin_invalid_arg_type)
3328 << ArgOrdinal << /* scalar or vector of */ 5 << /* no int */ 0
3329 << /* float */ 1 << PassedType;
3330 return false;
3331}
3332
3333static bool CheckFloatOrHalfRepresentation(Sema *S, SourceLocation Loc,
3334 int ArgOrdinal,
3335 clang::QualType PassedType) {
3336 clang::QualType BaseType = PassedType;
3337 if (const auto *VT = PassedType->getAs<clang::VectorType>())
3338 BaseType = VT->getElementType();
3339 else if (const auto *MT = PassedType->getAs<clang::MatrixType>())
3340 BaseType = MT->getElementType();
3341
3342 if (!BaseType->isHalfType() && !BaseType->isFloat32Type())
3343 return S->Diag(Loc, DiagID: diag::err_builtin_invalid_arg_type)
3344 << ArgOrdinal << /* scalar or vector of */ 5 << /* no int */ 0
3345 << /* half or float */ 2 << PassedType;
3346 return false;
3347}
3348
3349static bool CheckAnyDoubleRepresentation(Sema *S, SourceLocation Loc,
3350 int ArgOrdinal,
3351 clang::QualType PassedType) {
3352 clang::QualType BaseType =
3353 PassedType->isVectorType()
3354 ? PassedType->castAs<clang::VectorType>()->getElementType()
3355 : PassedType->isMatrixType()
3356 ? PassedType->castAs<clang::MatrixType>()->getElementType()
3357 : PassedType;
3358 if (!BaseType->isDoubleType()) {
3359 // FIXME: adopt standard `err_builtin_invalid_arg_type` instead of using
3360 // this custom error.
3361 return S->Diag(Loc, DiagID: diag::err_builtin_requires_double_type)
3362 << ArgOrdinal << PassedType;
3363 }
3364
3365 return false;
3366}
3367
3368static bool CheckModifiableLValue(Sema *S, CallExpr *TheCall,
3369 unsigned ArgIndex) {
3370 auto *Arg = TheCall->getArg(Arg: ArgIndex);
3371 SourceLocation OrigLoc = Arg->getExprLoc();
3372 if (Arg->IgnoreCasts()->isModifiableLvalue(Ctx&: S->Context, Loc: &OrigLoc) ==
3373 Expr::MLV_Valid)
3374 return false;
3375 S->Diag(Loc: OrigLoc, DiagID: diag::error_hlsl_inout_lvalue) << Arg << 0;
3376 return true;
3377}
3378
3379// Verifies that the argument at `ArgIndex` of `TheCall` refers to memory in
3380// one of `AllowedSpaces`. Intended for HLSL builtins (e.g. atomics).
3381static bool CheckArgAddrSpaceOneOf(Sema *S, CallExpr *TheCall,
3382 unsigned ArgIndex,
3383 ArrayRef<LangAS> AllowedSpaces) {
3384 Expr *Arg = TheCall->getArg(Arg: ArgIndex);
3385 QualType LValueTy = Arg->IgnoreCasts()->getType();
3386 if (llvm::is_contained(Range&: AllowedSpaces, Element: LValueTy.getAddressSpace()))
3387 return false;
3388 S->Diag(Loc: Arg->getBeginLoc(), DiagID: diag::err_hlsl_atomic_arg_addr_space)
3389 << (ArgIndex + 1) << LValueTy;
3390 return true;
3391}
3392
3393static bool CheckNoDoubleVectors(Sema *S, SourceLocation Loc, int ArgOrdinal,
3394 clang::QualType PassedType) {
3395 const auto *VecTy = PassedType->getAs<VectorType>();
3396 if (!VecTy)
3397 return false;
3398
3399 if (VecTy->getElementType()->isDoubleType())
3400 return S->Diag(Loc, DiagID: diag::err_builtin_invalid_arg_type)
3401 << ArgOrdinal << /* scalar */ 1 << /* no int */ 0 << /* fp */ 1
3402 << PassedType;
3403 return false;
3404}
3405
3406static bool CheckFloatingOrIntRepresentation(Sema *S, SourceLocation Loc,
3407 int ArgOrdinal,
3408 clang::QualType PassedType) {
3409 if (!PassedType->hasIntegerRepresentation() &&
3410 !PassedType->hasFloatingRepresentation())
3411 return S->Diag(Loc, DiagID: diag::err_builtin_invalid_arg_type)
3412 << ArgOrdinal << /* scalar or vector of */ 5 << /* integer */ 1
3413 << /* fp */ 1 << PassedType;
3414 return false;
3415}
3416
3417static bool CheckUnsignedIntVecRepresentation(Sema *S, SourceLocation Loc,
3418 int ArgOrdinal,
3419 clang::QualType PassedType) {
3420 if (auto *VecTy = PassedType->getAs<VectorType>())
3421 if (VecTy->getElementType()->isUnsignedIntegerType())
3422 return false;
3423
3424 return S->Diag(Loc, DiagID: diag::err_builtin_invalid_arg_type)
3425 << ArgOrdinal << /* vector of */ 4 << /* uint */ 3 << /* no fp */ 0
3426 << PassedType;
3427}
3428
3429// checks for unsigned ints of all sizes
3430static bool CheckUnsignedIntRepresentation(Sema *S, SourceLocation Loc,
3431 int ArgOrdinal,
3432 clang::QualType PassedType) {
3433 if (!PassedType->hasUnsignedIntegerRepresentation())
3434 return S->Diag(Loc, DiagID: diag::err_builtin_invalid_arg_type)
3435 << ArgOrdinal << /* scalar or vector of */ 5 << /* unsigned int */ 3
3436 << /* no fp */ 0 << PassedType;
3437 return false;
3438}
3439
3440static bool CheckExpectedBitWidth(Sema *S, CallExpr *TheCall,
3441 unsigned ArgOrdinal, unsigned Width) {
3442 QualType ArgTy = TheCall->getArg(Arg: 0)->getType();
3443 if (auto *VTy = ArgTy->getAs<VectorType>())
3444 ArgTy = VTy->getElementType();
3445 // ensure arg type has expected bit width
3446 uint64_t ElementBitCount =
3447 S->getASTContext().getTypeSizeInChars(T: ArgTy).getQuantity() * 8;
3448 if (ElementBitCount != Width) {
3449 S->Diag(Loc: TheCall->getArg(Arg: 0)->getBeginLoc(),
3450 DiagID: diag::err_integer_incorrect_bit_count)
3451 << Width << ElementBitCount;
3452 return true;
3453 }
3454 return false;
3455}
3456
3457static void SetElementTypeAsReturnType(Sema *S, CallExpr *TheCall,
3458 QualType ReturnType) {
3459 if (auto *VecTyA = TheCall->getArg(Arg: 0)->getType()->getAs<VectorType>())
3460 ReturnType =
3461 S->Context.getExtVectorType(VectorType: ReturnType, NumElts: VecTyA->getNumElements());
3462 else if (auto *MatTyA =
3463 TheCall->getArg(Arg: 0)->getType()->getAs<ConstantMatrixType>())
3464 ReturnType = S->Context.getConstantMatrixType(
3465 ElementType: ReturnType, NumRows: MatTyA->getNumRows(), NumColumns: MatTyA->getNumColumns());
3466
3467 TheCall->setType(ReturnType);
3468}
3469
3470static bool CheckScalarOrVector(Sema *S, CallExpr *TheCall, QualType Scalar,
3471 unsigned ArgIndex) {
3472 assert(TheCall->getNumArgs() >= ArgIndex);
3473 QualType ArgType = TheCall->getArg(Arg: ArgIndex)->getType();
3474 auto *VTy = ArgType->getAs<VectorType>();
3475 // not the scalar or vector<scalar>
3476 if (!(S->Context.hasSameUnqualifiedType(T1: ArgType, T2: Scalar) ||
3477 (VTy &&
3478 S->Context.hasSameUnqualifiedType(T1: VTy->getElementType(), T2: Scalar)))) {
3479 S->Diag(Loc: TheCall->getArg(Arg: 0)->getBeginLoc(),
3480 DiagID: diag::err_typecheck_expect_scalar_or_vector)
3481 << ArgType << Scalar;
3482 return true;
3483 }
3484 return false;
3485}
3486
3487static bool CheckScalarOrVectorOrMatrix(Sema *S, CallExpr *TheCall,
3488 QualType Scalar, unsigned ArgIndex) {
3489 assert(TheCall->getNumArgs() > ArgIndex);
3490
3491 Expr *Arg = TheCall->getArg(Arg: ArgIndex);
3492 QualType ArgType = Arg->getType();
3493
3494 // Scalar: T
3495 if (S->Context.hasSameUnqualifiedType(T1: ArgType, T2: Scalar))
3496 return false;
3497
3498 // Vector: vector<T>
3499 if (const auto *VTy = ArgType->getAs<VectorType>()) {
3500 if (S->Context.hasSameUnqualifiedType(T1: VTy->getElementType(), T2: Scalar))
3501 return false;
3502 }
3503
3504 // Matrix: ConstantMatrixType with element type T
3505 if (const auto *MTy = ArgType->getAs<ConstantMatrixType>()) {
3506 if (S->Context.hasSameUnqualifiedType(T1: MTy->getElementType(), T2: Scalar))
3507 return false;
3508 }
3509
3510 // Not a scalar/vector/matrix-of-scalar
3511 S->Diag(Loc: Arg->getBeginLoc(),
3512 DiagID: diag::err_typecheck_expect_scalar_or_vector_or_matrix)
3513 << ArgType << Scalar;
3514 return true;
3515}
3516
3517static bool CheckAnyScalarOrVector(Sema *S, CallExpr *TheCall,
3518 unsigned ArgIndex) {
3519 assert(TheCall->getNumArgs() >= ArgIndex);
3520 QualType ArgType = TheCall->getArg(Arg: ArgIndex)->getType();
3521 auto *VTy = ArgType->getAs<VectorType>();
3522 // not the scalar or vector<scalar>
3523 if (!(ArgType->isScalarType() ||
3524 (VTy && VTy->getElementType()->isScalarType()))) {
3525 S->Diag(Loc: TheCall->getArg(Arg: 0)->getBeginLoc(),
3526 DiagID: diag::err_typecheck_expect_any_scalar_or_vector)
3527 << ArgType << 1;
3528 return true;
3529 }
3530 return false;
3531}
3532
3533// Check that the argument is not a bool or vector<bool>
3534// Returns true on error
3535static bool CheckNotBoolScalarOrVector(Sema *S, CallExpr *TheCall,
3536 unsigned ArgIndex) {
3537 QualType BoolType = S->getASTContext().BoolTy;
3538 assert(ArgIndex < TheCall->getNumArgs());
3539 QualType ArgType = TheCall->getArg(Arg: ArgIndex)->getType();
3540 auto *VTy = ArgType->getAs<VectorType>();
3541 // is the bool or vector<bool>
3542 if (S->Context.hasSameUnqualifiedType(T1: ArgType, T2: BoolType) ||
3543 (VTy &&
3544 S->Context.hasSameUnqualifiedType(T1: VTy->getElementType(), T2: BoolType))) {
3545 S->Diag(Loc: TheCall->getArg(Arg: 0)->getBeginLoc(),
3546 DiagID: diag::err_typecheck_expect_any_scalar_or_vector)
3547 << ArgType << 0;
3548 return true;
3549 }
3550 return false;
3551}
3552
3553static bool CheckWaveActive(Sema *S, CallExpr *TheCall) {
3554 if (CheckNotBoolScalarOrVector(S, TheCall, ArgIndex: 0))
3555 return true;
3556 return false;
3557}
3558
3559static bool CheckWavePrefix(Sema *S, CallExpr *TheCall) {
3560 if (CheckNotBoolScalarOrVector(S, TheCall, ArgIndex: 0))
3561 return true;
3562 return false;
3563}
3564
3565static bool CheckBoolSelect(Sema *S, CallExpr *TheCall) {
3566 assert(TheCall->getNumArgs() == 3);
3567 Expr *Arg1 = TheCall->getArg(Arg: 1);
3568 Expr *Arg2 = TheCall->getArg(Arg: 2);
3569 if (!S->Context.hasSameUnqualifiedType(T1: Arg1->getType(), T2: Arg2->getType())) {
3570 S->Diag(Loc: TheCall->getBeginLoc(),
3571 DiagID: diag::err_typecheck_call_different_arg_types)
3572 << Arg1->getType() << Arg2->getType() << Arg1->getSourceRange()
3573 << Arg2->getSourceRange();
3574 return true;
3575 }
3576
3577 TheCall->setType(Arg1->getType());
3578 return false;
3579}
3580
3581static bool CheckVectorSelect(Sema *S, CallExpr *TheCall) {
3582 assert(TheCall->getNumArgs() == 3);
3583 Expr *Arg1 = TheCall->getArg(Arg: 1);
3584 QualType Arg1Ty = Arg1->getType();
3585 Expr *Arg2 = TheCall->getArg(Arg: 2);
3586 QualType Arg2Ty = Arg2->getType();
3587
3588 QualType Arg1ScalarTy = Arg1Ty;
3589 if (auto VTy = Arg1ScalarTy->getAs<VectorType>())
3590 Arg1ScalarTy = VTy->getElementType();
3591
3592 QualType Arg2ScalarTy = Arg2Ty;
3593 if (auto VTy = Arg2ScalarTy->getAs<VectorType>())
3594 Arg2ScalarTy = VTy->getElementType();
3595
3596 if (!S->Context.hasSameUnqualifiedType(T1: Arg1ScalarTy, T2: Arg2ScalarTy))
3597 S->Diag(Loc: Arg1->getBeginLoc(), DiagID: diag::err_hlsl_builtin_scalar_vector_mismatch)
3598 << /* second and third */ 1 << TheCall->getCallee() << Arg1Ty << Arg2Ty;
3599
3600 QualType Arg0Ty = TheCall->getArg(Arg: 0)->getType();
3601 unsigned Arg0Length = Arg0Ty->getAs<VectorType>()->getNumElements();
3602 unsigned Arg1Length = Arg1Ty->isVectorType()
3603 ? Arg1Ty->getAs<VectorType>()->getNumElements()
3604 : 0;
3605 unsigned Arg2Length = Arg2Ty->isVectorType()
3606 ? Arg2Ty->getAs<VectorType>()->getNumElements()
3607 : 0;
3608 if (Arg1Length > 0 && Arg0Length != Arg1Length) {
3609 S->Diag(Loc: TheCall->getBeginLoc(),
3610 DiagID: diag::err_typecheck_vector_lengths_not_equal)
3611 << Arg0Ty << Arg1Ty << TheCall->getArg(Arg: 0)->getSourceRange()
3612 << Arg1->getSourceRange();
3613 return true;
3614 }
3615
3616 if (Arg2Length > 0 && Arg0Length != Arg2Length) {
3617 S->Diag(Loc: TheCall->getBeginLoc(),
3618 DiagID: diag::err_typecheck_vector_lengths_not_equal)
3619 << Arg0Ty << Arg2Ty << TheCall->getArg(Arg: 0)->getSourceRange()
3620 << Arg2->getSourceRange();
3621 return true;
3622 }
3623
3624 TheCall->setType(
3625 S->getASTContext().getExtVectorType(VectorType: Arg1ScalarTy, NumElts: Arg0Length));
3626 return false;
3627}
3628
3629static QualType getVectorOrScalarType(Sema &S, QualType BaseType,
3630 unsigned Count) {
3631 return Count > 1 ? S.Context.getExtVectorType(VectorType: BaseType, NumElts: Count) : BaseType;
3632}
3633
3634static bool CheckScalarFloatOperand(Sema &S, CallExpr *TheCall,
3635 unsigned ArgIndex) {
3636 return CheckArgTypeMatches(S: &S, Arg: TheCall->getArg(Arg: ArgIndex), ExpectedType: S.Context.FloatTy);
3637}
3638
3639static bool CheckIndexType(Sema *S, CallExpr *TheCall, unsigned IndexArgIndex) {
3640 assert(TheCall->getNumArgs() > IndexArgIndex && "Index argument missing");
3641 QualType ArgType = TheCall->getArg(Arg: IndexArgIndex)->getType();
3642 QualType IndexTy = ArgType;
3643 unsigned int ActualDim = 1;
3644 if (const auto *VTy = IndexTy->getAs<VectorType>()) {
3645 ActualDim = VTy->getNumElements();
3646 IndexTy = VTy->getElementType();
3647 }
3648 if (!IndexTy->isIntegerType()) {
3649 S->Diag(Loc: TheCall->getArg(Arg: IndexArgIndex)->getBeginLoc(),
3650 DiagID: diag::err_typecheck_expect_int)
3651 << ArgType;
3652 return true;
3653 }
3654
3655 QualType ResourceArgTy = TheCall->getArg(Arg: 0)->getType();
3656 const HLSLAttributedResourceType *ResTy =
3657 ResourceArgTy.getTypePtr()->getAs<HLSLAttributedResourceType>();
3658 assert(ResTy && "Resource argument must be a resource");
3659 HLSLAttributedResourceType::Attributes ResAttrs = ResTy->getAttrs();
3660
3661 unsigned int ExpectedDim = 1;
3662 if (ResAttrs.ResourceDimension != llvm::dxil::ResourceDimension::Unknown)
3663 ExpectedDim = getResourceDimensions(Dim: ResAttrs.ResourceDimension) +
3664 (ResAttrs.IsArray ? 1 : 0);
3665
3666 if (ActualDim != ExpectedDim) {
3667 S->Diag(Loc: TheCall->getArg(Arg: IndexArgIndex)->getBeginLoc(),
3668 DiagID: diag::err_hlsl_builtin_resource_coordinate_dimension_mismatch)
3669 << cast<NamedDecl>(Val: TheCall->getCalleeDecl()) << ExpectedDim
3670 << ActualDim;
3671 return true;
3672 }
3673
3674 return false;
3675}
3676
3677static bool CheckResourceHandle(
3678 Sema *S, CallExpr *TheCall, unsigned ArgIndex,
3679 llvm::function_ref<bool(const HLSLAttributedResourceType *ResType)> Check =
3680 nullptr) {
3681 assert(TheCall->getNumArgs() >= ArgIndex);
3682 QualType ArgType = TheCall->getArg(Arg: ArgIndex)->getType();
3683 const HLSLAttributedResourceType *ResTy =
3684 ArgType.getTypePtr()->getAs<HLSLAttributedResourceType>();
3685 if (!ResTy) {
3686 S->Diag(Loc: TheCall->getArg(Arg: ArgIndex)->getBeginLoc(),
3687 DiagID: diag::err_typecheck_expect_hlsl_resource)
3688 << ArgType;
3689 return true;
3690 }
3691 if (Check && Check(ResTy)) {
3692 S->Diag(Loc: TheCall->getArg(Arg: ArgIndex)->getExprLoc(),
3693 DiagID: diag::err_invalid_hlsl_resource_type)
3694 << ArgType;
3695 return true;
3696 }
3697 return false;
3698}
3699
3700static QualType createCounterHandleType(ASTContext &AST,
3701 QualType MainHandleTy) {
3702 assert(MainHandleTy->isHLSLAttributedResourceType() &&
3703 "expected resource handle type");
3704 auto *MainResType = MainHandleTy->getAs<HLSLAttributedResourceType>();
3705 auto MainAttrs = MainResType->getAttrs();
3706 assert(!MainAttrs.IsCounter && "cannot create a counter from a counter");
3707 MainAttrs.IsCounter = true;
3708 return AST.getHLSLAttributedResourceType(Wrapped: MainResType->getWrappedType(),
3709 Contained: MainResType->getContainedType(),
3710 Attrs: MainAttrs);
3711}
3712
3713enum class SampleKind { Sample, Bias, Grad, Level, Cmp, CmpLevelZero };
3714
3715static StringRef getSampleMethodName(SampleKind Kind) {
3716 switch (Kind) {
3717 case SampleKind::Sample:
3718 return "Sample";
3719 case SampleKind::Bias:
3720 return "SampleBias";
3721 case SampleKind::Grad:
3722 return "SampleGrad";
3723 case SampleKind::Level:
3724 return "SampleLevel";
3725 case SampleKind::Cmp:
3726 return "SampleCmp";
3727 case SampleKind::CmpLevelZero:
3728 return "SampleCmpLevelZero";
3729 }
3730 llvm_unreachable("Invalid SampleKind");
3731}
3732
3733// Returns the name of the resource method whose body the sampling or gather
3734// builtin is being emitted into, which is the name the user called. This
3735// matters for methods that share a builtin, like 'Gather' and 'GatherRed'.
3736// Falls back to DefaultName if the builtin is used outside of a resource
3737// method.
3738static StringRef getCurrentResourceMethodName(Sema &S, StringRef DefaultName) {
3739 const auto *MD = dyn_cast_if_present<CXXMethodDecl>(Val: S.getCurFunctionDecl());
3740 if (!MD || !MD->getDeclName().isIdentifier())
3741 return DefaultName;
3742
3743 QualType RecordTy = S.Context.getCanonicalTagType(TD: MD->getParent());
3744 if (!RecordTy->isHLSLResourceRecord())
3745 return DefaultName;
3746
3747 return MD->getName();
3748}
3749
3750// Returns the element type of a typed resource's contained type. Typed resource
3751// element types are scalars or vectors of scalars, so anything that is not a
3752// vector is already the element type.
3753static QualType getTypedResourceElementType(QualType ContainedType) {
3754 if (const auto *VecTy = ContainedType->getAs<VectorType>())
3755 return VecTy->getElementType();
3756 return ContainedType;
3757}
3758
3759// Sampling from and gathering on resources with a 'double' element type is not
3760// supported. Such resources are still valid declarations whose contents can be
3761// accessed by other means, like Load or the subscript operator.
3762static bool CheckNoDoubleElementType(Sema &S, CallExpr *TheCall,
3763 QualType ContainedType,
3764 StringRef DefaultName) {
3765 QualType EltTy = getTypedResourceElementType(ContainedType);
3766 if (!EltTy->isSpecificBuiltinType(K: BuiltinType::Double))
3767 return false;
3768
3769 S.Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_hlsl_sample_double_element_type)
3770 << getCurrentResourceMethodName(S, DefaultName) << ContainedType;
3771 return true;
3772}
3773
3774// Sampling textures with an integer element type was introduced in SM 6.7 as
3775// part of Advanced Texture Operations. The shader model only applies to DirectX
3776// targets; Vulkan has no such restriction.
3777static bool CheckIntegerElementTypeShaderModel(Sema &S, CallExpr *TheCall,
3778 QualType ContainedType,
3779 SampleKind Kind) {
3780 // Comparison sampling requires a floating point element type at every shader
3781 // model, which the caller diagnoses.
3782 if (Kind == SampleKind::Cmp || Kind == SampleKind::CmpLevelZero)
3783 return false;
3784
3785 // 'bool' is an integer type in HLSL, but sampling bool resources is never
3786 // allowed, so it must not be reported as requiring shader model 6.7.
3787 QualType EltTy = getTypedResourceElementType(ContainedType);
3788 if (!EltTy->isIntegerType() || EltTy->isBooleanType())
3789 return false;
3790
3791 const TargetInfo &TI = S.Context.getTargetInfo();
3792 if (!TI.getTriple().isDXIL())
3793 return false;
3794
3795 VersionTuple SMVersion = TI.getPlatformMinVersion();
3796 if (SMVersion >= VersionTuple(6, 7))
3797 return false;
3798
3799 S.Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_hlsl_sample_integer_element_type)
3800 << getCurrentResourceMethodName(S, DefaultName: getSampleMethodName(Kind))
3801 << ContainedType << SMVersion.getAsString();
3802 return true;
3803}
3804
3805static bool CheckTextureSamplerAndLocation(Sema &S, CallExpr *TheCall,
3806 bool IncludeArraySlice = true) {
3807 // Check the texture handle.
3808 if (CheckResourceHandle(S: &S, TheCall, ArgIndex: 0,
3809 Check: [](const HLSLAttributedResourceType *ResType) {
3810 return ResType->getAttrs().ResourceDimension ==
3811 llvm::dxil::ResourceDimension::Unknown;
3812 }))
3813 return true;
3814
3815 // Check the sampler handle.
3816 if (CheckResourceHandle(S: &S, TheCall, ArgIndex: 1,
3817 Check: [](const HLSLAttributedResourceType *ResType) {
3818 return ResType->getAttrs().ResourceClass !=
3819 llvm::hlsl::ResourceClass::Sampler;
3820 }))
3821 return true;
3822
3823 auto *ResourceTy =
3824 TheCall->getArg(Arg: 0)->getType()->castAs<HLSLAttributedResourceType>();
3825
3826 // Check the location.
3827 unsigned ExpectedDim =
3828 getResourceDimensions(Dim: ResourceTy->getAttrs().ResourceDimension) +
3829 (IncludeArraySlice && ResourceTy->getAttrs().IsArray ? 1 : 0);
3830 if (CheckArgTypeMatches(
3831 S: &S, Arg: TheCall->getArg(Arg: 2),
3832 ExpectedType: getVectorOrScalarType(S, BaseType: S.Context.FloatTy, Count: ExpectedDim)))
3833 return true;
3834
3835 return false;
3836}
3837
3838static bool CheckCalculateLodBuiltin(Sema &S, CallExpr *TheCall) {
3839 if (S.checkArgCount(Call: TheCall, DesiredArgCount: 3))
3840 return true;
3841
3842 // CalculateLevelOfDetail location uses resource dimension only (e.g. float2
3843 // for 2D), not an extra array slice component like Sample/Gather.
3844 if (CheckTextureSamplerAndLocation(S, TheCall, /*IncludeArraySlice=*/false))
3845 return true;
3846
3847 TheCall->setType(S.Context.FloatTy);
3848 return false;
3849}
3850
3851static bool CheckGatherBuiltin(Sema &S, CallExpr *TheCall, bool IsCmp) {
3852 if (S.checkArgCountRange(Call: TheCall, MinArgCount: IsCmp ? 5 : 4, MaxArgCount: IsCmp ? 6 : 5))
3853 return true;
3854
3855 if (CheckTextureSamplerAndLocation(S, TheCall))
3856 return true;
3857
3858 unsigned NextIdx = 3;
3859 if (IsCmp) {
3860 // Check the compare value.
3861 if (CheckScalarFloatOperand(S, TheCall, ArgIndex: NextIdx))
3862 return true;
3863 NextIdx++;
3864 }
3865
3866 // Check the component operand.
3867 if (CheckArgTypeMatches(S: &S, Arg: TheCall->getArg(Arg: NextIdx),
3868 ExpectedType: S.Context.UnsignedIntTy))
3869 return true;
3870 Expr *ComponentArg = TheCall->getArg(Arg: NextIdx);
3871
3872 // GatherCmp operations on Vulkan target must use component 0 (Red).
3873 if (IsCmp && S.getASTContext().getTargetInfo().getTriple().isSPIRV()) {
3874 std::optional<llvm::APSInt> ComponentOpt =
3875 ComponentArg->getIntegerConstantExpr(Ctx: S.getASTContext());
3876 if (ComponentOpt) {
3877 int64_t ComponentVal = ComponentOpt->getSExtValue();
3878 if (ComponentVal != 0) {
3879 // Issue an error if the component is not 0 (Red).
3880 // 0 -> Red, 1 -> Green, 2 -> Blue, 3 -> Alpha
3881 assert(ComponentVal >= 0 && ComponentVal <= 3 &&
3882 "The component is not in the expected range.");
3883 S.Diag(Loc: ComponentArg->getBeginLoc(),
3884 DiagID: diag::err_hlsl_gathercmp_invalid_component)
3885 << ComponentVal;
3886 return true;
3887 }
3888 }
3889 }
3890
3891 NextIdx++;
3892
3893 // Check the offset operand.
3894 const HLSLAttributedResourceType *ResourceTy =
3895 TheCall->getArg(Arg: 0)->getType()->castAs<HLSLAttributedResourceType>();
3896 if (TheCall->getNumArgs() > NextIdx) {
3897 unsigned ExpectedDim =
3898 getResourceDimensions(Dim: ResourceTy->getAttrs().ResourceDimension);
3899 if (CheckArgTypeMatches(
3900 S: &S, Arg: TheCall->getArg(Arg: NextIdx),
3901 ExpectedType: getVectorOrScalarType(S, BaseType: S.Context.IntTy, Count: ExpectedDim)))
3902 return true;
3903 NextIdx++;
3904 }
3905
3906 assert(ResourceTy->hasContainedType() &&
3907 "Expecting a contained type for resource with a dimension "
3908 "attribute.");
3909 QualType ReturnType = ResourceTy->getContainedType();
3910
3911 if (CheckNoDoubleElementType(S, TheCall, ContainedType: ReturnType,
3912 DefaultName: IsCmp ? "GatherCmp" : "Gather"))
3913 return true;
3914
3915 if (IsCmp) {
3916 if (!ReturnType->hasFloatingRepresentation()) {
3917 S.Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_hlsl_samplecmp_requires_float);
3918 return true;
3919 }
3920 }
3921
3922 if (const auto *VecTy = ReturnType->getAs<VectorType>())
3923 ReturnType = VecTy->getElementType();
3924 ReturnType = S.Context.getExtVectorType(VectorType: ReturnType, NumElts: 4);
3925
3926 TheCall->setType(ReturnType);
3927
3928 return false;
3929}
3930static bool CheckLoadLevelBuiltin(Sema &S, CallExpr *TheCall) {
3931 if (S.checkArgCountRange(Call: TheCall, MinArgCount: 2, MaxArgCount: 3))
3932 return true;
3933
3934 // Check the texture handle.
3935 if (CheckResourceHandle(S: &S, TheCall, ArgIndex: 0,
3936 Check: [](const HLSLAttributedResourceType *ResType) {
3937 return ResType->getAttrs().ResourceDimension ==
3938 llvm::dxil::ResourceDimension::Unknown;
3939 }))
3940 return true;
3941
3942 auto *ResourceTy =
3943 TheCall->getArg(Arg: 0)->getType()->castAs<HLSLAttributedResourceType>();
3944
3945 // A UAV descriptor binds a single mip slice, so a RWTexture location has no
3946 // mip component to select, and TextureLoad on a UAV takes no offset.
3947 bool IsUAV =
3948 ResourceTy->getAttrs().ResourceClass == llvm::dxil::ResourceClass::UAV;
3949 if (IsUAV && S.checkArgCount(Call: TheCall, DesiredArgCount: 2))
3950 return true;
3951
3952 // Check the location: int3 for Texture2D and int4 for Texture2DArray, which
3953 // both carry a trailing mip level; int2 and int3 for the RWTexture forms,
3954 // which do not.
3955 unsigned ResourceDim =
3956 getResourceDimensions(Dim: ResourceTy->getAttrs().ResourceDimension);
3957 unsigned LocationDim = ResourceDim + (ResourceTy->getAttrs().IsArray ? 1 : 0);
3958 if (!IsUAV)
3959 ++LocationDim;
3960 if (CheckArgTypeMatches(
3961 S: &S, Arg: TheCall->getArg(Arg: 1),
3962 ExpectedType: getVectorOrScalarType(S, BaseType: S.Context.IntTy, Count: LocationDim)))
3963 return true;
3964
3965 // Check the offset operand (int2 for 2D textures; no array slice).
3966 if (TheCall->getNumArgs() > 2) {
3967 if (CheckArgTypeMatches(
3968 S: &S, Arg: TheCall->getArg(Arg: 2),
3969 ExpectedType: getVectorOrScalarType(S, BaseType: S.Context.IntTy, Count: ResourceDim)))
3970 return true;
3971 }
3972
3973 TheCall->setType(ResourceTy->getContainedType());
3974 return false;
3975}
3976
3977static bool CheckLoadMSBuiltin(Sema &S, CallExpr *TheCall) {
3978 if (S.checkArgCountRange(Call: TheCall, MinArgCount: 3, MaxArgCount: 4))
3979 return true;
3980
3981 // Check the multisampled texture handle.
3982 if (CheckResourceHandle(S: &S, TheCall, ArgIndex: 0,
3983 Check: [](const HLSLAttributedResourceType *ResType) {
3984 return !ResType->isMultiSampled();
3985 }))
3986 return true;
3987
3988 auto *ResourceTy =
3989 TheCall->getArg(Arg: 0)->getType()->castAs<HLSLAttributedResourceType>();
3990
3991 // Check the location (int2 for Texture2DMS, int3 for Texture2DMSArray).
3992 // Unlike Load on regular textures, there is no mip/LOD component.
3993 unsigned ResourceDim =
3994 getResourceDimensions(Dim: ResourceTy->getAttrs().ResourceDimension);
3995 unsigned LocationDim = ResourceDim + (ResourceTy->getAttrs().IsArray ? 1 : 0);
3996 if (CheckArgTypeMatches(
3997 S: &S, Arg: TheCall->getArg(Arg: 1),
3998 ExpectedType: getVectorOrScalarType(S, BaseType: S.Context.IntTy, Count: LocationDim)))
3999 return true;
4000
4001 // Check the sample index operand (scalar int).
4002 if (CheckArgTypeMatches(S: &S, Arg: TheCall->getArg(Arg: 2), ExpectedType: S.Context.IntTy))
4003 return true;
4004
4005 // Check the offset operand (int2 for 2D textures; no array slice).
4006 if (TheCall->getNumArgs() > 3) {
4007 if (CheckArgTypeMatches(
4008 S: &S, Arg: TheCall->getArg(Arg: 3),
4009 ExpectedType: getVectorOrScalarType(S, BaseType: S.Context.IntTy, Count: ResourceDim)))
4010 return true;
4011 }
4012
4013 TheCall->setType(ResourceTy->getContainedType());
4014 return false;
4015}
4016
4017static bool CheckSamplingBuiltin(Sema &S, CallExpr *TheCall, SampleKind Kind) {
4018 unsigned MinArgs, MaxArgs;
4019 if (Kind == SampleKind::Sample) {
4020 MinArgs = 3;
4021 MaxArgs = 5;
4022 } else if (Kind == SampleKind::Bias) {
4023 MinArgs = 4;
4024 MaxArgs = 6;
4025 } else if (Kind == SampleKind::Grad) {
4026 MinArgs = 5;
4027 MaxArgs = 7;
4028 } else if (Kind == SampleKind::Level) {
4029 MinArgs = 4;
4030 MaxArgs = 5;
4031 } else if (Kind == SampleKind::Cmp) {
4032 MinArgs = 4;
4033 MaxArgs = 6;
4034 } else {
4035 assert(Kind == SampleKind::CmpLevelZero);
4036 MinArgs = 4;
4037 MaxArgs = 5;
4038 }
4039
4040 if (S.checkArgCountRange(Call: TheCall, MinArgCount: MinArgs, MaxArgCount: MaxArgs))
4041 return true;
4042
4043 if (CheckTextureSamplerAndLocation(S, TheCall))
4044 return true;
4045
4046 const HLSLAttributedResourceType *ResourceTy =
4047 TheCall->getArg(Arg: 0)->getType()->castAs<HLSLAttributedResourceType>();
4048 unsigned ExpectedDim =
4049 getResourceDimensions(Dim: ResourceTy->getAttrs().ResourceDimension);
4050
4051 unsigned NextIdx = 3;
4052 if (Kind == SampleKind::Bias || Kind == SampleKind::Level ||
4053 Kind == SampleKind::Cmp || Kind == SampleKind::CmpLevelZero) {
4054 // Check the bias, lod level, or compare value, depending on the kind.
4055 // All of them must be a scalar float value.
4056 if (CheckScalarFloatOperand(S, TheCall, ArgIndex: NextIdx))
4057 return true;
4058 NextIdx++;
4059 } else if (Kind == SampleKind::Grad) {
4060 QualType GradTy = getVectorOrScalarType(S, BaseType: S.Context.FloatTy, Count: ExpectedDim);
4061
4062 // Check the DDX operand.
4063 if (CheckArgTypeMatches(S: &S, Arg: TheCall->getArg(Arg: NextIdx), ExpectedType: GradTy))
4064 return true;
4065
4066 // Check the DDY operand.
4067 if (CheckArgTypeMatches(S: &S, Arg: TheCall->getArg(Arg: NextIdx + 1), ExpectedType: GradTy))
4068 return true;
4069 NextIdx += 2;
4070 }
4071
4072 // Check the offset operand (if applicable).
4073 if (hasResourceOffset(Dim: ResourceTy->getAttrs().ResourceDimension) &&
4074 TheCall->getNumArgs() > NextIdx) {
4075 if (CheckArgTypeMatches(
4076 S: &S, Arg: TheCall->getArg(Arg: NextIdx),
4077 ExpectedType: getVectorOrScalarType(S, BaseType: S.Context.IntTy, Count: ExpectedDim)))
4078 return true;
4079 NextIdx++;
4080 }
4081
4082 // Check the clamp operand.
4083 if (Kind != SampleKind::Level && Kind != SampleKind::CmpLevelZero &&
4084 TheCall->getNumArgs() > NextIdx) {
4085 if (CheckScalarFloatOperand(S, TheCall, ArgIndex: NextIdx))
4086 return true;
4087 }
4088
4089 assert(ResourceTy->hasContainedType() &&
4090 "Expecting a contained type for resource with a dimension "
4091 "attribute.");
4092 QualType ReturnType = ResourceTy->getContainedType();
4093
4094 if (CheckNoDoubleElementType(S, TheCall, ContainedType: ReturnType,
4095 DefaultName: getSampleMethodName(Kind)))
4096 return true;
4097
4098 if (CheckIntegerElementTypeShaderModel(S, TheCall, ContainedType: ReturnType, Kind))
4099 return true;
4100
4101 if (Kind == SampleKind::Cmp || Kind == SampleKind::CmpLevelZero) {
4102 if (!ReturnType->hasFloatingRepresentation()) {
4103 S.Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_hlsl_samplecmp_requires_float);
4104 return true;
4105 }
4106 ReturnType = S.Context.FloatTy;
4107 }
4108 TheCall->setType(ReturnType);
4109
4110 return false;
4111}
4112
4113// Note: returning true in this case results in CheckBuiltinFunctionCall
4114// returning an ExprError
4115bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
4116 switch (BuiltinID) {
4117 case Builtin::BI__builtin_hlsl_adduint64: {
4118 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 2))
4119 return true;
4120
4121 if (CheckAllArgTypesAreCorrect(S: &SemaRef, TheCall,
4122 Check: CheckUnsignedIntVecRepresentation))
4123 return true;
4124
4125 // ensure arg integers are 32-bits
4126 if (CheckExpectedBitWidth(S: &SemaRef, TheCall, ArgOrdinal: 0, Width: 32))
4127 return true;
4128
4129 // ensure both args are vectors of total bit size of a multiple of 64
4130 auto *VTy = TheCall->getArg(Arg: 0)->getType()->getAs<VectorType>();
4131 int NumElementsArg = VTy->getNumElements();
4132 if (NumElementsArg != 2 && NumElementsArg != 4) {
4133 SemaRef.Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_vector_incorrect_bit_count)
4134 << 1 /*a multiple of*/ << 64 << NumElementsArg * 32;
4135 return true;
4136 }
4137
4138 // ensure first arg and second arg have the same type
4139 if (CheckAllArgsHaveSameType(S: &SemaRef, TheCall))
4140 return true;
4141
4142 ExprResult A = TheCall->getArg(Arg: 0);
4143 QualType ArgTyA = A.get()->getType();
4144 // return type is the same as the input type
4145 TheCall->setType(ArgTyA);
4146 break;
4147 }
4148 case Builtin::BI__builtin_hlsl_resource_getpointer: {
4149 if (SemaRef.checkArgCountRange(Call: TheCall, MinArgCount: 1, MaxArgCount: 2) ||
4150 CheckResourceHandle(S: &SemaRef, TheCall, ArgIndex: 0) ||
4151 (TheCall->getNumArgs() == 2 && CheckIndexType(S: &SemaRef, TheCall, IndexArgIndex: 1)))
4152 return true;
4153
4154 auto *ResourceTy =
4155 TheCall->getArg(Arg: 0)->getType()->castAs<HLSLAttributedResourceType>();
4156 QualType ContainedTy = ResourceTy->getContainedType();
4157 auto ReturnType = SemaRef.Context.getAddrSpaceQualType(
4158 T: ContainedTy,
4159 AddressSpace: getLangASFromResourceClass(RC: ResourceTy->getAttrs().ResourceClass));
4160 ReturnType = SemaRef.Context.getPointerType(T: ReturnType);
4161 TheCall->setType(ReturnType);
4162
4163 break;
4164 }
4165 case Builtin::BI__builtin_hlsl_resource_getpointer_typed: {
4166 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 3) ||
4167 CheckResourceHandle(S: &SemaRef, TheCall, ArgIndex: 0) ||
4168 CheckIndexType(S: &SemaRef, TheCall, IndexArgIndex: 1))
4169 return true;
4170
4171 QualType ElementTy = TheCall->getArg(Arg: 2)->getType();
4172 assert(ElementTy->isPointerType() &&
4173 "expected pointer type for second argument");
4174 ElementTy = ElementTy->getPointeeType();
4175
4176 // Reject array types
4177 if (ElementTy->isArrayType())
4178 return SemaRef.Diag(
4179 Loc: cast<FunctionDecl>(Val: SemaRef.CurContext)->getPointOfInstantiation(),
4180 DiagID: diag::err_invalid_use_of_array_type);
4181
4182 auto *ResourceTy =
4183 TheCall->getArg(Arg: 0)->getType()->castAs<HLSLAttributedResourceType>();
4184 auto ReturnType = SemaRef.Context.getAddrSpaceQualType(
4185 T: ElementTy,
4186 AddressSpace: getLangASFromResourceClass(RC: ResourceTy->getAttrs().ResourceClass));
4187 ReturnType = SemaRef.Context.getPointerType(T: ReturnType);
4188 TheCall->setType(ReturnType);
4189
4190 break;
4191 }
4192 case Builtin::BI__builtin_hlsl_transpose_if_memory_is_row_major: {
4193 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 2) ||
4194 CheckArgTypeMatches(S: &SemaRef, Arg: TheCall->getArg(Arg: 1),
4195 ExpectedType: SemaRef.getASTContext().IntTy))
4196 return true;
4197
4198 TheCall->setType(TheCall->getArg(Arg: 0)->getType());
4199
4200 break;
4201 }
4202 case Builtin::BI__builtin_hlsl_resource_load_with_status: {
4203 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 3) ||
4204 CheckResourceHandle(S: &SemaRef, TheCall, ArgIndex: 0) ||
4205 CheckArgTypeMatches(S: &SemaRef, Arg: TheCall->getArg(Arg: 1),
4206 ExpectedType: SemaRef.getASTContext().UnsignedIntTy) ||
4207 CheckArgTypeMatches(S: &SemaRef, Arg: TheCall->getArg(Arg: 2),
4208 ExpectedType: SemaRef.getASTContext().UnsignedIntTy) ||
4209 CheckModifiableLValue(S: &SemaRef, TheCall, ArgIndex: 2))
4210 return true;
4211
4212 auto *ResourceTy =
4213 TheCall->getArg(Arg: 0)->getType()->castAs<HLSLAttributedResourceType>();
4214 QualType ReturnType = ResourceTy->getContainedType();
4215 TheCall->setType(ReturnType);
4216
4217 break;
4218 }
4219 case Builtin::BI__builtin_hlsl_resource_load_with_status_typed: {
4220 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 4) ||
4221 CheckResourceHandle(S: &SemaRef, TheCall, ArgIndex: 0) ||
4222 CheckArgTypeMatches(S: &SemaRef, Arg: TheCall->getArg(Arg: 1),
4223 ExpectedType: SemaRef.getASTContext().UnsignedIntTy) ||
4224 CheckArgTypeMatches(S: &SemaRef, Arg: TheCall->getArg(Arg: 2),
4225 ExpectedType: SemaRef.getASTContext().UnsignedIntTy) ||
4226 CheckModifiableLValue(S: &SemaRef, TheCall, ArgIndex: 2))
4227 return true;
4228
4229 QualType ReturnType = TheCall->getArg(Arg: 3)->getType();
4230 assert(ReturnType->isPointerType() &&
4231 "expected pointer type for second argument");
4232 ReturnType = ReturnType->getPointeeType();
4233
4234 // Reject array types
4235 if (ReturnType->isArrayType())
4236 return SemaRef.Diag(
4237 Loc: cast<FunctionDecl>(Val: SemaRef.CurContext)->getPointOfInstantiation(),
4238 DiagID: diag::err_invalid_use_of_array_type);
4239
4240 TheCall->setType(ReturnType);
4241
4242 break;
4243 }
4244 case Builtin::BI__builtin_hlsl_resource_load_level:
4245 return CheckLoadLevelBuiltin(S&: SemaRef, TheCall);
4246 case Builtin::BI__builtin_hlsl_resource_load_ms:
4247 return CheckLoadMSBuiltin(S&: SemaRef, TheCall);
4248 case Builtin::BI__builtin_hlsl_resource_sample:
4249 return CheckSamplingBuiltin(S&: SemaRef, TheCall, Kind: SampleKind::Sample);
4250 case Builtin::BI__builtin_hlsl_resource_sample_bias:
4251 return CheckSamplingBuiltin(S&: SemaRef, TheCall, Kind: SampleKind::Bias);
4252 case Builtin::BI__builtin_hlsl_resource_sample_grad:
4253 return CheckSamplingBuiltin(S&: SemaRef, TheCall, Kind: SampleKind::Grad);
4254 case Builtin::BI__builtin_hlsl_resource_sample_level:
4255 return CheckSamplingBuiltin(S&: SemaRef, TheCall, Kind: SampleKind::Level);
4256 case Builtin::BI__builtin_hlsl_resource_sample_cmp:
4257 return CheckSamplingBuiltin(S&: SemaRef, TheCall, Kind: SampleKind::Cmp);
4258 case Builtin::BI__builtin_hlsl_resource_sample_cmp_level_zero:
4259 return CheckSamplingBuiltin(S&: SemaRef, TheCall, Kind: SampleKind::CmpLevelZero);
4260 case Builtin::BI__builtin_hlsl_resource_calculate_lod:
4261 case Builtin::BI__builtin_hlsl_resource_calculate_lod_unclamped:
4262 return CheckCalculateLodBuiltin(S&: SemaRef, TheCall);
4263 case Builtin::BI__builtin_hlsl_resource_gather:
4264 return CheckGatherBuiltin(S&: SemaRef, TheCall, /*IsCmp=*/false);
4265 case Builtin::BI__builtin_hlsl_resource_gather_cmp:
4266 return CheckGatherBuiltin(S&: SemaRef, TheCall, /*IsCmp=*/true);
4267 case Builtin::BI__builtin_hlsl_resource_uninitializedhandle: {
4268 assert(TheCall->getNumArgs() == 1 && "expected 1 arg");
4269 // Update return type to be the attributed resource type from arg0.
4270 QualType ResourceTy = TheCall->getArg(Arg: 0)->getType();
4271 TheCall->setType(ResourceTy);
4272 break;
4273 }
4274 case Builtin::BI__builtin_hlsl_resource_handlefrombinding: {
4275 assert(TheCall->getNumArgs() == 6 && "expected 6 args");
4276 // Update return type to be the attributed resource type from arg0.
4277 QualType ResourceTy = TheCall->getArg(Arg: 0)->getType();
4278 TheCall->setType(ResourceTy);
4279 break;
4280 }
4281 case Builtin::BI__builtin_hlsl_resource_handlefromimplicitbinding: {
4282 assert(TheCall->getNumArgs() == 6 && "expected 6 args");
4283 // Update return type to be the attributed resource type from arg0.
4284 QualType ResourceTy = TheCall->getArg(Arg: 0)->getType();
4285 TheCall->setType(ResourceTy);
4286 break;
4287 }
4288 case Builtin::BI__builtin_hlsl_resource_counterhandlefromimplicitbinding: {
4289 assert(TheCall->getNumArgs() == 3 && "expected 3 args");
4290 QualType MainHandleTy = TheCall->getArg(Arg: 0)->getType();
4291 // Update return type to be the attributed resource type from arg0
4292 // with added IsCounter flag.
4293 QualType CounterHandleTy =
4294 createCounterHandleType(AST&: SemaRef.getASTContext(), MainHandleTy);
4295 TheCall->setType(CounterHandleTy);
4296 break;
4297 }
4298 case Builtin::BI__builtin_hlsl_and:
4299 case Builtin::BI__builtin_hlsl_or: {
4300 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 2))
4301 return true;
4302 if (CheckScalarOrVectorOrMatrix(S: &SemaRef, TheCall, Scalar: getASTContext().BoolTy,
4303 ArgIndex: 0))
4304 return true;
4305 if (CheckAllArgsHaveSameType(S: &SemaRef, TheCall))
4306 return true;
4307
4308 ExprResult A = TheCall->getArg(Arg: 0);
4309 QualType ArgTyA = A.get()->getType();
4310 // return type is the same as the input type
4311 TheCall->setType(ArgTyA);
4312 break;
4313 }
4314 case Builtin::BI__builtin_hlsl_all:
4315 case Builtin::BI__builtin_hlsl_any: {
4316 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1))
4317 return true;
4318 if (CheckAnyScalarOrVector(S: &SemaRef, TheCall, ArgIndex: 0))
4319 return true;
4320 break;
4321 }
4322 case Builtin::BI__builtin_hlsl_asdouble: {
4323 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 2))
4324 return true;
4325 if (CheckScalarOrVector(
4326 S: &SemaRef, TheCall,
4327 /*only check for uint*/ Scalar: SemaRef.Context.UnsignedIntTy,
4328 /* arg index */ ArgIndex: 0))
4329 return true;
4330 if (CheckScalarOrVector(
4331 S: &SemaRef, TheCall,
4332 /*only check for uint*/ Scalar: SemaRef.Context.UnsignedIntTy,
4333 /* arg index */ ArgIndex: 1))
4334 return true;
4335 if (CheckAllArgsHaveSameType(S: &SemaRef, TheCall))
4336 return true;
4337
4338 SetElementTypeAsReturnType(S: &SemaRef, TheCall, ReturnType: getASTContext().DoubleTy);
4339 break;
4340 }
4341 case Builtin::BI__builtin_hlsl_elementwise_clamp: {
4342 if (SemaRef.BuiltinElementwiseTernaryMath(
4343 TheCall, /*ArgTyRestr=*/
4344 Sema::EltwiseBuiltinArgTyRestriction::None))
4345 return true;
4346 break;
4347 }
4348 case Builtin::BI__builtin_hlsl_dot: {
4349 // arg count is checked by BuiltinVectorToScalarMath
4350 if (SemaRef.BuiltinVectorToScalarMath(TheCall))
4351 return true;
4352 if (CheckAllArgTypesAreCorrect(S: &SemaRef, TheCall, Check: CheckNoDoubleVectors))
4353 return true;
4354 break;
4355 }
4356 case Builtin::BI__builtin_hlsl_elementwise_firstbithigh:
4357 case Builtin::BI__builtin_hlsl_elementwise_firstbitlow: {
4358 if (SemaRef.PrepareBuiltinElementwiseMathOneArgCall(TheCall))
4359 return true;
4360
4361 const Expr *Arg = TheCall->getArg(Arg: 0);
4362 QualType ArgTy = Arg->getType();
4363 QualType EltTy = ArgTy;
4364
4365 QualType ResTy = SemaRef.Context.UnsignedIntTy;
4366
4367 if (auto *VecTy = EltTy->getAs<VectorType>()) {
4368 EltTy = VecTy->getElementType();
4369 ResTy = SemaRef.Context.getExtVectorType(VectorType: ResTy, NumElts: VecTy->getNumElements());
4370 }
4371
4372 if (!EltTy->isIntegerType()) {
4373 Diag(Loc: Arg->getBeginLoc(), DiagID: diag::err_builtin_invalid_arg_type)
4374 << 1 << /* scalar or vector of */ 5 << /* integer ty */ 1
4375 << /* no fp */ 0 << ArgTy;
4376 return true;
4377 }
4378
4379 TheCall->setType(ResTy);
4380 break;
4381 }
4382 case Builtin::BI__builtin_hlsl_select: {
4383 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 3))
4384 return true;
4385 if (CheckScalarOrVector(S: &SemaRef, TheCall, Scalar: getASTContext().BoolTy, ArgIndex: 0))
4386 return true;
4387 QualType ArgTy = TheCall->getArg(Arg: 0)->getType();
4388 if (ArgTy->isBooleanType() && CheckBoolSelect(S: &SemaRef, TheCall))
4389 return true;
4390 auto *VTy = ArgTy->getAs<VectorType>();
4391 if (VTy && VTy->getElementType()->isBooleanType() &&
4392 CheckVectorSelect(S: &SemaRef, TheCall))
4393 return true;
4394 break;
4395 }
4396 case Builtin::BI__builtin_hlsl_elementwise_saturate:
4397 case Builtin::BI__builtin_hlsl_elementwise_rcp: {
4398 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1))
4399 return true;
4400 if (!TheCall->getArg(Arg: 0)
4401 ->getType()
4402 ->hasFloatingRepresentation()) // half or float or double
4403 return SemaRef.Diag(Loc: TheCall->getArg(Arg: 0)->getBeginLoc(),
4404 DiagID: diag::err_builtin_invalid_arg_type)
4405 << /* ordinal */ 1 << /* scalar or vector */ 5 << /* no int */ 0
4406 << /* fp */ 1 << TheCall->getArg(Arg: 0)->getType();
4407 if (SemaRef.PrepareBuiltinElementwiseMathOneArgCall(TheCall))
4408 return true;
4409 break;
4410 }
4411 case Builtin::BI__builtin_hlsl_elementwise_rsqrt:
4412 case Builtin::BI__builtin_hlsl_elementwise_frac:
4413 case Builtin::BI__builtin_hlsl_elementwise_ddx_coarse:
4414 case Builtin::BI__builtin_hlsl_elementwise_ddy_coarse:
4415 case Builtin::BI__builtin_hlsl_elementwise_ddx_fine:
4416 case Builtin::BI__builtin_hlsl_elementwise_ddy_fine: {
4417 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1))
4418 return true;
4419 if (CheckAllArgTypesAreCorrect(S: &SemaRef, TheCall,
4420 Check: CheckFloatOrHalfRepresentation))
4421 return true;
4422 if (SemaRef.PrepareBuiltinElementwiseMathOneArgCall(TheCall))
4423 return true;
4424 break;
4425 }
4426 case Builtin::BI__builtin_hlsl_elementwise_isinf:
4427 case Builtin::BI__builtin_hlsl_elementwise_isnan: {
4428 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1))
4429 return true;
4430 if (CheckAllArgTypesAreCorrect(S: &SemaRef, TheCall,
4431 Check: CheckFloatOrHalfRepresentation))
4432 return true;
4433 if (SemaRef.PrepareBuiltinElementwiseMathOneArgCall(TheCall))
4434 return true;
4435 SetElementTypeAsReturnType(S: &SemaRef, TheCall, ReturnType: getASTContext().BoolTy);
4436 break;
4437 }
4438 case Builtin::BI__builtin_hlsl_mad: {
4439 if (SemaRef.BuiltinElementwiseTernaryMath(
4440 TheCall, /*ArgTyRestr=*/
4441 Sema::EltwiseBuiltinArgTyRestriction::None))
4442 return true;
4443 break;
4444 }
4445 case Builtin::BI__builtin_hlsl_mul: {
4446 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 2))
4447 return true;
4448
4449 Expr *Arg0 = TheCall->getArg(Arg: 0);
4450 Expr *Arg1 = TheCall->getArg(Arg: 1);
4451 QualType Ty0 = Arg0->getType();
4452 QualType Ty1 = Arg1->getType();
4453
4454 auto getElemType = [](QualType T) -> QualType {
4455 if (const auto *VTy = T->getAs<VectorType>())
4456 return VTy->getElementType();
4457 if (const auto *MTy = T->getAs<ConstantMatrixType>())
4458 return MTy->getElementType();
4459 return T;
4460 };
4461
4462 QualType EltTy0 = getElemType(Ty0);
4463
4464 bool IsVec0 = Ty0->isVectorType();
4465 bool IsMat0 = Ty0->isConstantMatrixType();
4466 bool IsVec1 = Ty1->isVectorType();
4467 bool IsMat1 = Ty1->isConstantMatrixType();
4468
4469 QualType RetTy;
4470
4471 if (IsVec0 && IsMat1) {
4472 auto *MatTy = Ty1->castAs<ConstantMatrixType>();
4473 RetTy = getASTContext().getExtVectorType(VectorType: EltTy0, NumElts: MatTy->getNumColumns());
4474 } else if (IsMat0 && IsVec1) {
4475 auto *MatTy = Ty0->castAs<ConstantMatrixType>();
4476 RetTy = getASTContext().getExtVectorType(VectorType: EltTy0, NumElts: MatTy->getNumRows());
4477 } else {
4478 assert(IsMat0 && IsMat1);
4479 auto *MatTy0 = Ty0->castAs<ConstantMatrixType>();
4480 auto *MatTy1 = Ty1->castAs<ConstantMatrixType>();
4481 RetTy = getASTContext().getConstantMatrixType(
4482 ElementType: EltTy0, NumRows: MatTy0->getNumRows(), NumColumns: MatTy1->getNumColumns());
4483 }
4484
4485 TheCall->setType(RetTy);
4486 break;
4487 }
4488 case Builtin::BI__builtin_elementwise_fma: {
4489 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 3) ||
4490 CheckAllArgsHaveSameType(S: &SemaRef, TheCall)) {
4491 return true;
4492 }
4493
4494 if (CheckAllArgTypesAreCorrect(S: &SemaRef, TheCall,
4495 Check: CheckAnyDoubleRepresentation))
4496 return true;
4497
4498 ExprResult A = TheCall->getArg(Arg: 0);
4499 QualType ArgTyA = A.get()->getType();
4500 // return type is the same as input type
4501 TheCall->setType(ArgTyA);
4502 break;
4503 }
4504 case Builtin::BI__builtin_hlsl_transpose: {
4505 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1))
4506 return true;
4507
4508 Expr *Arg = TheCall->getArg(Arg: 0);
4509 QualType ArgTy = Arg->getType();
4510
4511 const auto *MatTy = ArgTy->getAs<ConstantMatrixType>();
4512 if (!MatTy) {
4513 SemaRef.Diag(Loc: Arg->getBeginLoc(), DiagID: diag::err_builtin_invalid_arg_type)
4514 << 1 << /* matrix */ 3 << /* no int */ 0 << /* no fp */ 0 << ArgTy;
4515 return true;
4516 }
4517
4518 QualType RetTy = getASTContext().getConstantMatrixType(
4519 ElementType: MatTy->getElementType(), NumRows: MatTy->getNumColumns(), NumColumns: MatTy->getNumRows());
4520 TheCall->setType(RetTy);
4521 break;
4522 }
4523 case Builtin::BI__builtin_hlsl_elementwise_sign: {
4524 if (SemaRef.PrepareBuiltinElementwiseMathOneArgCall(TheCall))
4525 return true;
4526 if (CheckAllArgTypesAreCorrect(S: &SemaRef, TheCall,
4527 Check: CheckFloatingOrIntRepresentation))
4528 return true;
4529 SetElementTypeAsReturnType(S: &SemaRef, TheCall, ReturnType: getASTContext().IntTy);
4530 break;
4531 }
4532 case Builtin::BI__builtin_hlsl_wave_active_all_equal: {
4533 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1))
4534 return true;
4535
4536 // Ensure input expr type is a scalar/vector
4537 if (CheckAnyScalarOrVector(S: &SemaRef, TheCall, ArgIndex: 0))
4538 return true;
4539
4540 QualType InputTy = TheCall->getArg(Arg: 0)->getType();
4541 ASTContext &Ctx = getASTContext();
4542
4543 QualType RetTy;
4544
4545 // If vector, construct bool vector of same size
4546 if (const auto *VecTy = InputTy->getAs<ExtVectorType>()) {
4547 unsigned NumElts = VecTy->getNumElements();
4548 RetTy = Ctx.getExtVectorType(VectorType: Ctx.BoolTy, NumElts);
4549 } else {
4550 // Scalar case
4551 RetTy = Ctx.BoolTy;
4552 }
4553
4554 TheCall->setType(RetTy);
4555 break;
4556 }
4557 case Builtin::BI__builtin_hlsl_wave_active_max:
4558 case Builtin::BI__builtin_hlsl_wave_active_min:
4559 case Builtin::BI__builtin_hlsl_wave_active_sum:
4560 case Builtin::BI__builtin_hlsl_wave_active_product: {
4561 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1))
4562 return true;
4563
4564 // Ensure input expr type is a scalar/vector and the same as the return type
4565 if (CheckAnyScalarOrVector(S: &SemaRef, TheCall, ArgIndex: 0))
4566 return true;
4567 if (CheckWaveActive(S: &SemaRef, TheCall))
4568 return true;
4569 ExprResult Expr = TheCall->getArg(Arg: 0);
4570 QualType ArgTyExpr = Expr.get()->getType();
4571 TheCall->setType(ArgTyExpr);
4572 break;
4573 }
4574 case Builtin::BI__builtin_hlsl_wave_active_bit_or:
4575 case Builtin::BI__builtin_hlsl_wave_active_bit_xor:
4576 case Builtin::BI__builtin_hlsl_wave_active_bit_and: {
4577 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1))
4578 return true;
4579
4580 // Ensure input expr type is a scalar/vector
4581 if (CheckAnyScalarOrVector(S: &SemaRef, TheCall, ArgIndex: 0))
4582 return true;
4583
4584 if (CheckWaveActive(S: &SemaRef, TheCall))
4585 return true;
4586
4587 // Ensure the expr type is interpretable as a uint or vector<uint>
4588 ExprResult Expr = TheCall->getArg(Arg: 0);
4589 QualType ArgTyExpr = Expr.get()->getType();
4590 auto *VTy = ArgTyExpr->getAs<VectorType>();
4591 if (!(ArgTyExpr->isIntegerType() ||
4592 (VTy && VTy->getElementType()->isIntegerType()))) {
4593 SemaRef.Diag(Loc: TheCall->getArg(Arg: 0)->getBeginLoc(),
4594 DiagID: diag::err_builtin_invalid_arg_type)
4595 << ArgTyExpr << SemaRef.Context.UnsignedIntTy << 1 << 0 << 0;
4596 return true;
4597 }
4598
4599 // Ensure input expr type is the same as the return type
4600 TheCall->setType(ArgTyExpr);
4601 break;
4602 }
4603 case Builtin::BI__builtin_hlsl_interlocked_add:
4604 case Builtin::BI__builtin_hlsl_interlocked_and:
4605 case Builtin::BI__builtin_hlsl_interlocked_min:
4606 case Builtin::BI__builtin_hlsl_interlocked_or:
4607 case Builtin::BI__builtin_hlsl_interlocked_xor: {
4608 // The builtin's prototype in Builtins.td is `void (...)`, so direct calls
4609 // to `__builtin_hlsl_interlocked_op` bypass argument checking entirely.
4610 // When reached via the synthesized `InterlockedOp` overload set in
4611 // HLSLExternalSemaSource, overload resolution has already enforced the
4612 // argument count, integer-type matching, and the address-space requirement
4613 // on `dest`. The checks below are a safety net for callers that invoke the
4614 // builtin by its mangled name and would otherwise reach CodeGen unchecked.
4615 if (TheCall->getNumArgs() < 2) {
4616 SemaRef.Diag(Loc: TheCall->getEndLoc(),
4617 DiagID: diag::err_typecheck_call_too_few_args_at_least)
4618 << /*callee_type=*/0 << /*min_arg_count=*/2 << TheCall->getNumArgs()
4619 << /*is_non_object=*/0 << TheCall->getSourceRange();
4620 return true;
4621 }
4622 if (SemaRef.checkArgCountAtMost(Call: TheCall, MaxArgCount: 3))
4623 return true;
4624
4625 QualType DestTy = TheCall->getArg(Arg: 0)->getType().getUnqualifiedType();
4626 if (!DestTy->isIntegerType()) {
4627 SemaRef.Diag(Loc: TheCall->getArg(Arg: 0)->getBeginLoc(),
4628 DiagID: diag::err_builtin_invalid_arg_type)
4629 << /*ordinal=*/1 << /*scalar*/ 1 << /*integer*/ 1 << /*no float*/ 0
4630 << DestTy;
4631 return true;
4632 }
4633
4634 // 64-bit interlocked ops require SM 6.6 on DXIL. The synthesized wrapper
4635 // methods (e.g. RWByteAddressBuffer::InterlockedAdd64) are only declared
4636 // on SM 6.6+, so this defensive check only fires for direct builtin
4637 // calls; skip synthetic invocations (invalid source location).
4638 const TargetInfo &TI = SemaRef.Context.getTargetInfo();
4639 if (TheCall->getBeginLoc().isValid() &&
4640 TI.getTriple().getArch() == llvm::Triple::dxil &&
4641 SemaRef.Context.getTypeSize(T: DestTy) == 64 &&
4642 TI.getPlatformMinVersion() < VersionTuple(6, 6)) {
4643 SemaRef.Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_hlsl_builtin_requires_sm)
4644 << TheCall->getDirectCallee() << VersionTuple(6, 6).getAsString();
4645 return true;
4646 }
4647
4648 if (CheckModifiableLValue(S: &SemaRef, TheCall, ArgIndex: 0))
4649 return true;
4650
4651 if (CheckArgAddrSpaceOneOf(S: &SemaRef, TheCall, ArgIndex: 0,
4652 AllowedSpaces: {LangAS::hlsl_groupshared, LangAS::hlsl_device}))
4653 return true;
4654
4655 if (CheckArgTypeMatches(S: &SemaRef, Arg: TheCall->getArg(Arg: 1), ExpectedType: DestTy))
4656 return true;
4657
4658 if (TheCall->getNumArgs() == 3) {
4659 if (CheckArgTypeMatches(S: &SemaRef, Arg: TheCall->getArg(Arg: 2), ExpectedType: DestTy))
4660 return true;
4661 if (CheckModifiableLValue(S: &SemaRef, TheCall, ArgIndex: 2))
4662 return true;
4663 }
4664
4665 TheCall->setType(SemaRef.Context.VoidTy);
4666 break;
4667 }
4668 // Note these are llvm builtins that we want to catch invalid intrinsic
4669 // generation. Normal handling of these builtins will occur elsewhere.
4670 case Builtin::BI__builtin_elementwise_bitreverse: {
4671 // does not include a check for number of arguments
4672 // because that is done previously
4673 if (CheckAllArgTypesAreCorrect(S: &SemaRef, TheCall,
4674 Check: CheckUnsignedIntRepresentation))
4675 return true;
4676 break;
4677 }
4678 case Builtin::BI__builtin_hlsl_wave_prefix_count_bits: {
4679 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1))
4680 return true;
4681
4682 QualType ArgType = TheCall->getArg(Arg: 0)->getType();
4683
4684 if (!(ArgType->isScalarType())) {
4685 SemaRef.Diag(Loc: TheCall->getArg(Arg: 0)->getBeginLoc(),
4686 DiagID: diag::err_typecheck_expect_any_scalar_or_vector)
4687 << ArgType << 0;
4688 return true;
4689 }
4690
4691 if (!(ArgType->isBooleanType())) {
4692 SemaRef.Diag(Loc: TheCall->getArg(Arg: 0)->getBeginLoc(),
4693 DiagID: diag::err_typecheck_expect_any_scalar_or_vector)
4694 << ArgType << 0;
4695 return true;
4696 }
4697
4698 break;
4699 }
4700 case Builtin::BI__builtin_hlsl_wave_read_lane_at: {
4701 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 2))
4702 return true;
4703
4704 // Ensure index parameter type can be interpreted as a uint
4705 ExprResult Index = TheCall->getArg(Arg: 1);
4706 QualType ArgTyIndex = Index.get()->getType();
4707 if (!ArgTyIndex->isIntegerType()) {
4708 SemaRef.Diag(Loc: TheCall->getArg(Arg: 1)->getBeginLoc(),
4709 DiagID: diag::err_typecheck_convert_incompatible)
4710 << ArgTyIndex << SemaRef.Context.UnsignedIntTy << 1 << 0 << 0;
4711 return true;
4712 }
4713
4714 // Ensure input expr type is a scalar/vector and the same as the return type
4715 if (CheckAnyScalarOrVector(S: &SemaRef, TheCall, ArgIndex: 0))
4716 return true;
4717
4718 ExprResult Expr = TheCall->getArg(Arg: 0);
4719 QualType ArgTyExpr = Expr.get()->getType();
4720 TheCall->setType(ArgTyExpr);
4721 break;
4722 }
4723 case Builtin::BI__builtin_hlsl_wave_get_lane_index: {
4724 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 0))
4725 return true;
4726 break;
4727 }
4728 case Builtin::BI__builtin_hlsl_wave_prefix_sum:
4729 case Builtin::BI__builtin_hlsl_wave_prefix_product: {
4730 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1))
4731 return true;
4732
4733 // Ensure input expr type is a scalar/vector and the same as the return type
4734 if (CheckAnyScalarOrVector(S: &SemaRef, TheCall, ArgIndex: 0))
4735 return true;
4736 if (CheckWavePrefix(S: &SemaRef, TheCall))
4737 return true;
4738 ExprResult Expr = TheCall->getArg(Arg: 0);
4739 QualType ArgTyExpr = Expr.get()->getType();
4740 TheCall->setType(ArgTyExpr);
4741 break;
4742 }
4743 case Builtin::BI__builtin_hlsl_quad_read_across_x:
4744 case Builtin::BI__builtin_hlsl_quad_read_across_y:
4745 case Builtin::BI__builtin_hlsl_quad_read_across_diagonal: {
4746 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1))
4747 return true;
4748
4749 if (CheckAnyScalarOrVector(S: &SemaRef, TheCall, ArgIndex: 0))
4750 return true;
4751 if (CheckNotBoolScalarOrVector(S: &SemaRef, TheCall, ArgIndex: 0))
4752 return true;
4753 ExprResult Expr = TheCall->getArg(Arg: 0);
4754 QualType ArgTyExpr = Expr.get()->getType();
4755 TheCall->setType(ArgTyExpr);
4756 break;
4757 }
4758 case Builtin::BI__builtin_hlsl_elementwise_splitdouble: {
4759 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 3))
4760 return true;
4761
4762 if (CheckScalarOrVectorOrMatrix(S: &SemaRef, TheCall, Scalar: SemaRef.Context.DoubleTy,
4763 ArgIndex: 0) ||
4764 CheckScalarOrVectorOrMatrix(S: &SemaRef, TheCall,
4765 Scalar: SemaRef.Context.UnsignedIntTy, ArgIndex: 1) ||
4766 CheckScalarOrVectorOrMatrix(S: &SemaRef, TheCall,
4767 Scalar: SemaRef.Context.UnsignedIntTy, ArgIndex: 2))
4768 return true;
4769
4770 if (CheckModifiableLValue(S: &SemaRef, TheCall, ArgIndex: 1) ||
4771 CheckModifiableLValue(S: &SemaRef, TheCall, ArgIndex: 2))
4772 return true;
4773 break;
4774 }
4775 case Builtin::BI__builtin_hlsl_elementwise_clip: {
4776 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1))
4777 return true;
4778
4779 if (CheckScalarOrVector(S: &SemaRef, TheCall, Scalar: SemaRef.Context.FloatTy, ArgIndex: 0))
4780 return true;
4781 break;
4782 }
4783 case Builtin::BI__builtin_elementwise_acos:
4784 case Builtin::BI__builtin_elementwise_asin:
4785 case Builtin::BI__builtin_elementwise_atan:
4786 case Builtin::BI__builtin_elementwise_atan2:
4787 case Builtin::BI__builtin_elementwise_ceil:
4788 case Builtin::BI__builtin_elementwise_cos:
4789 case Builtin::BI__builtin_elementwise_cosh:
4790 case Builtin::BI__builtin_elementwise_exp:
4791 case Builtin::BI__builtin_elementwise_exp2:
4792 case Builtin::BI__builtin_elementwise_exp10:
4793 case Builtin::BI__builtin_elementwise_floor:
4794 case Builtin::BI__builtin_elementwise_fmod:
4795 case Builtin::BI__builtin_elementwise_log:
4796 case Builtin::BI__builtin_elementwise_log2:
4797 case Builtin::BI__builtin_elementwise_log10:
4798 case Builtin::BI__builtin_elementwise_pow:
4799 case Builtin::BI__builtin_elementwise_roundeven:
4800 case Builtin::BI__builtin_elementwise_sin:
4801 case Builtin::BI__builtin_elementwise_sinh:
4802 case Builtin::BI__builtin_elementwise_sqrt:
4803 case Builtin::BI__builtin_elementwise_tan:
4804 case Builtin::BI__builtin_elementwise_tanh:
4805 case Builtin::BI__builtin_elementwise_trunc: {
4806 if (CheckAllArgTypesAreCorrect(S: &SemaRef, TheCall,
4807 Check: CheckFloatOrHalfRepresentation))
4808 return true;
4809 break;
4810 }
4811 case Builtin::BI__builtin_hlsl_buffer_update_counter: {
4812 assert(TheCall->getNumArgs() == 2 && "expected 2 args");
4813 auto checkResTy = [](const HLSLAttributedResourceType *ResTy) -> bool {
4814 return !(ResTy->getAttrs().ResourceClass == ResourceClass::UAV &&
4815 ResTy->getAttrs().RawBuffer && ResTy->hasContainedType());
4816 };
4817 if (CheckResourceHandle(S: &SemaRef, TheCall, ArgIndex: 0, Check: checkResTy))
4818 return true;
4819 Expr *OffsetExpr = TheCall->getArg(Arg: 1);
4820 std::optional<llvm::APSInt> Offset =
4821 OffsetExpr->getIntegerConstantExpr(Ctx: SemaRef.getASTContext());
4822 if (!Offset.has_value() || std::abs(i: Offset->getExtValue()) != 1) {
4823 SemaRef.Diag(Loc: TheCall->getArg(Arg: 1)->getBeginLoc(),
4824 DiagID: diag::err_hlsl_expect_arg_const_int_one_or_neg_one)
4825 << 1;
4826 return true;
4827 }
4828 break;
4829 }
4830 case Builtin::BI__builtin_hlsl_elementwise_f16tof32: {
4831 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1))
4832 return true;
4833 if (CheckAllArgTypesAreCorrect(S: &SemaRef, TheCall,
4834 Check: CheckUnsignedIntRepresentation))
4835 return true;
4836 // ensure arg integers are 32 bits
4837 if (CheckExpectedBitWidth(S: &SemaRef, TheCall, ArgOrdinal: 0, Width: 32))
4838 return true;
4839 // check it wasn't a bool type
4840 QualType ArgTy = TheCall->getArg(Arg: 0)->getType();
4841 if (auto *VTy = ArgTy->getAs<VectorType>())
4842 ArgTy = VTy->getElementType();
4843 if (ArgTy->isBooleanType()) {
4844 SemaRef.Diag(Loc: TheCall->getArg(Arg: 0)->getBeginLoc(),
4845 DiagID: diag::err_builtin_invalid_arg_type)
4846 << 1 << /* scalar or vector of */ 5 << /* unsigned int */ 3
4847 << /* no fp */ 0 << TheCall->getArg(Arg: 0)->getType();
4848 return true;
4849 }
4850
4851 SetElementTypeAsReturnType(S: &SemaRef, TheCall, ReturnType: getASTContext().FloatTy);
4852 break;
4853 }
4854 case Builtin::BI__builtin_hlsl_elementwise_f32tof16: {
4855 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1))
4856 return true;
4857 if (CheckAllArgTypesAreCorrect(S: &SemaRef, TheCall, Check: CheckFloatRepresentation))
4858 return true;
4859 SetElementTypeAsReturnType(S: &SemaRef, TheCall,
4860 ReturnType: getASTContext().UnsignedIntTy);
4861 break;
4862 }
4863 }
4864 return false;
4865}
4866
4867static void BuildFlattenedTypeList(QualType BaseTy,
4868 llvm::SmallVectorImpl<QualType> &List) {
4869 llvm::SmallVector<QualType, 16> WorkList;
4870 WorkList.push_back(Elt: BaseTy);
4871 while (!WorkList.empty()) {
4872 QualType T = WorkList.pop_back_val();
4873 T = T.getCanonicalType().getUnqualifiedType();
4874 if (const auto *AT = dyn_cast<ConstantArrayType>(Val&: T)) {
4875 llvm::SmallVector<QualType, 16> ElementFields;
4876 // Generally I've avoided recursion in this algorithm, but arrays of
4877 // structs could be time-consuming to flatten and churn through on the
4878 // work list. Hopefully nesting arrays of structs containing arrays
4879 // of structs too many levels deep is unlikely.
4880 BuildFlattenedTypeList(BaseTy: AT->getElementType(), List&: ElementFields);
4881 // Repeat the element's field list n times.
4882 for (uint64_t Ct = 0; Ct < AT->getZExtSize(); ++Ct)
4883 llvm::append_range(C&: List, R&: ElementFields);
4884 continue;
4885 }
4886 // Vectors can only have element types that are builtin types, so this can
4887 // add directly to the list instead of to the WorkList.
4888 if (const auto *VT = dyn_cast<VectorType>(Val&: T)) {
4889 List.insert(I: List.end(), NumToInsert: VT->getNumElements(), Elt: VT->getElementType());
4890 continue;
4891 }
4892 if (const auto *MT = dyn_cast<ConstantMatrixType>(Val&: T)) {
4893 List.insert(I: List.end(), NumToInsert: MT->getNumElementsFlattened(),
4894 Elt: MT->getElementType());
4895 continue;
4896 }
4897 if (const auto *RD = T->getAsCXXRecordDecl()) {
4898 if (RD->isStandardLayout())
4899 RD = RD->getStandardLayoutBaseWithFields();
4900
4901 // For types that we shouldn't decompose (unions and non-aggregates), just
4902 // add the type itself to the list.
4903 if (RD->isUnion() || !RD->isAggregate()) {
4904 List.push_back(Elt: T);
4905 continue;
4906 }
4907
4908 llvm::SmallVector<QualType, 16> FieldTypes;
4909 for (const auto *FD : RD->fields())
4910 if (!FD->isUnnamedBitField())
4911 FieldTypes.push_back(Elt: FD->getType());
4912 // Reverse the newly added sub-range.
4913 std::reverse(first: FieldTypes.begin(), last: FieldTypes.end());
4914 llvm::append_range(C&: WorkList, R&: FieldTypes);
4915
4916 // If this wasn't a standard layout type we may also have some base
4917 // classes to deal with.
4918 if (!RD->isStandardLayout()) {
4919 FieldTypes.clear();
4920 for (const auto &Base : RD->bases())
4921 FieldTypes.push_back(Elt: Base.getType());
4922 std::reverse(first: FieldTypes.begin(), last: FieldTypes.end());
4923 llvm::append_range(C&: WorkList, R&: FieldTypes);
4924 }
4925 continue;
4926 }
4927 List.push_back(Elt: T);
4928 }
4929}
4930
4931bool SemaHLSL::IsConstantBufferElementCompatible(clang::QualType QT) {
4932 if (QT.isNull())
4933 return false;
4934
4935 // Must be a class/struct.
4936 const auto *RD = QT->getAsCXXRecordDecl();
4937 if (!RD || RD->isUnion())
4938 return false;
4939
4940 // Cannot be a resource type or contain one.
4941 return !QT->isHLSLIntangibleType();
4942}
4943
4944bool SemaHLSL::IsTypedResourceElementCompatible(clang::QualType QT) {
4945 // null and array types are not allowed.
4946 if (QT.isNull() || QT->isArrayType())
4947 return false;
4948
4949 // UDT types are not allowed
4950 if (QT->isRecordType())
4951 return false;
4952
4953 if (QT->isBooleanType() || QT->isEnumeralType())
4954 return false;
4955
4956 // the only other valid builtin types are scalars or vectors
4957 if (QT->isArithmeticType()) {
4958 if (SemaRef.Context.getTypeSize(T: QT) / 8 > 16)
4959 return false;
4960 return true;
4961 }
4962
4963 if (const VectorType *VT = QT->getAs<VectorType>()) {
4964 int ArraySize = VT->getNumElements();
4965
4966 if (ArraySize > 4)
4967 return false;
4968
4969 QualType ElTy = VT->getElementType();
4970 if (ElTy->isBooleanType())
4971 return false;
4972
4973 if (SemaRef.Context.getTypeSize(T: QT) / 8 > 16)
4974 return false;
4975 return true;
4976 }
4977
4978 return false;
4979}
4980
4981bool SemaHLSL::IsScalarizedLayoutCompatible(QualType T1, QualType T2) const {
4982 if (T1.isNull() || T2.isNull())
4983 return false;
4984
4985 T1 = T1.getCanonicalType().getUnqualifiedType();
4986 T2 = T2.getCanonicalType().getUnqualifiedType();
4987
4988 // If both types are the same canonical type, they're obviously compatible.
4989 if (SemaRef.getASTContext().hasSameType(T1, T2))
4990 return true;
4991
4992 llvm::SmallVector<QualType, 16> T1Types;
4993 BuildFlattenedTypeList(BaseTy: T1, List&: T1Types);
4994 llvm::SmallVector<QualType, 16> T2Types;
4995 BuildFlattenedTypeList(BaseTy: T2, List&: T2Types);
4996
4997 // Check the flattened type list
4998 return llvm::equal(LRange&: T1Types, RRange&: T2Types,
4999 P: [this](QualType LHS, QualType RHS) -> bool {
5000 return SemaRef.IsLayoutCompatible(T1: LHS, T2: RHS);
5001 });
5002}
5003
5004bool SemaHLSL::CheckCompatibleParameterABI(FunctionDecl *New,
5005 FunctionDecl *Old) {
5006 if (New->getNumParams() != Old->getNumParams())
5007 return true;
5008
5009 bool HadError = false;
5010
5011 for (unsigned i = 0, e = New->getNumParams(); i != e; ++i) {
5012 ParmVarDecl *NewParam = New->getParamDecl(i);
5013 ParmVarDecl *OldParam = Old->getParamDecl(i);
5014
5015 // HLSL parameter declarations for inout and out must match between
5016 // declarations. In HLSL inout and out are ambiguous at the call site,
5017 // but have different calling behavior, so you cannot overload a
5018 // method based on a difference between inout and out annotations.
5019 const auto *NDAttr = NewParam->getAttr<HLSLParamModifierAttr>();
5020 unsigned NSpellingIdx = (NDAttr ? NDAttr->getSpellingListIndex() : 0);
5021 const auto *ODAttr = OldParam->getAttr<HLSLParamModifierAttr>();
5022 unsigned OSpellingIdx = (ODAttr ? ODAttr->getSpellingListIndex() : 0);
5023
5024 if (NSpellingIdx != OSpellingIdx) {
5025 SemaRef.Diag(Loc: NewParam->getLocation(),
5026 DiagID: diag::err_hlsl_param_qualifier_mismatch)
5027 << NDAttr << NewParam;
5028 SemaRef.Diag(Loc: OldParam->getLocation(), DiagID: diag::note_previous_declaration_as)
5029 << ODAttr;
5030 HadError = true;
5031 }
5032 }
5033 return HadError;
5034}
5035
5036// Generally follows PerformScalarCast, with cases reordered for
5037// clarity of what types are supported
5038bool SemaHLSL::CanPerformScalarCast(QualType SrcTy, QualType DestTy) {
5039
5040 if (!SrcTy->isScalarType() || !DestTy->isScalarType())
5041 return false;
5042
5043 if (SemaRef.getASTContext().hasSameUnqualifiedType(T1: SrcTy, T2: DestTy))
5044 return true;
5045
5046 switch (SrcTy->getScalarTypeKind()) {
5047 case Type::STK_Bool: // casting from bool is like casting from an integer
5048 case Type::STK_Integral:
5049 switch (DestTy->getScalarTypeKind()) {
5050 case Type::STK_Bool:
5051 case Type::STK_Integral:
5052 case Type::STK_Floating:
5053 return true;
5054 case Type::STK_CPointer:
5055 case Type::STK_ObjCObjectPointer:
5056 case Type::STK_BlockPointer:
5057 case Type::STK_MemberPointer:
5058 llvm_unreachable("HLSL doesn't support pointers.");
5059 case Type::STK_IntegralComplex:
5060 case Type::STK_FloatingComplex:
5061 llvm_unreachable("HLSL doesn't support complex types.");
5062 case Type::STK_FixedPoint:
5063 llvm_unreachable("HLSL doesn't support fixed point types.");
5064 }
5065 llvm_unreachable("Should have returned before this");
5066
5067 case Type::STK_Floating:
5068 switch (DestTy->getScalarTypeKind()) {
5069 case Type::STK_Floating:
5070 case Type::STK_Bool:
5071 case Type::STK_Integral:
5072 return true;
5073 case Type::STK_FloatingComplex:
5074 case Type::STK_IntegralComplex:
5075 llvm_unreachable("HLSL doesn't support complex types.");
5076 case Type::STK_FixedPoint:
5077 llvm_unreachable("HLSL doesn't support fixed point types.");
5078 case Type::STK_CPointer:
5079 case Type::STK_ObjCObjectPointer:
5080 case Type::STK_BlockPointer:
5081 case Type::STK_MemberPointer:
5082 llvm_unreachable("HLSL doesn't support pointers.");
5083 }
5084 llvm_unreachable("Should have returned before this");
5085
5086 case Type::STK_MemberPointer:
5087 case Type::STK_CPointer:
5088 case Type::STK_BlockPointer:
5089 case Type::STK_ObjCObjectPointer:
5090 llvm_unreachable("HLSL doesn't support pointers.");
5091
5092 case Type::STK_FixedPoint:
5093 llvm_unreachable("HLSL doesn't support fixed point types.");
5094
5095 case Type::STK_FloatingComplex:
5096 case Type::STK_IntegralComplex:
5097 llvm_unreachable("HLSL doesn't support complex types.");
5098 }
5099
5100 llvm_unreachable("Unhandled scalar cast");
5101}
5102
5103// Can perform an HLSL Aggregate splat cast if the Dest is an aggregate and the
5104// Src is a scalar, a vector of length 1, or a 1x1 matrix
5105// Or if Dest is a vector and Src is a vector of length 1 or a 1x1 matrix
5106bool SemaHLSL::CanPerformAggregateSplatCast(Expr *Src, QualType DestTy) {
5107
5108 QualType SrcTy = Src->getType();
5109 // Not a valid HLSL Aggregate Splat cast if Dest is a scalar or if this is
5110 // going to be a vector splat from a scalar.
5111 if ((SrcTy->isScalarType() && DestTy->isVectorType()) ||
5112 DestTy->isScalarType())
5113 return false;
5114
5115 const VectorType *SrcVecTy = SrcTy->getAs<VectorType>();
5116 const ConstantMatrixType *SrcMatTy = SrcTy->getAs<ConstantMatrixType>();
5117
5118 // Src isn't a scalar, a vector of length 1, or a 1x1 matrix
5119 if (!SrcTy->isScalarType() &&
5120 !(SrcVecTy && SrcVecTy->getNumElements() == 1) &&
5121 !(SrcMatTy && SrcMatTy->getNumElementsFlattened() == 1))
5122 return false;
5123
5124 if (SrcVecTy)
5125 SrcTy = SrcVecTy->getElementType();
5126 else if (SrcMatTy)
5127 SrcTy = SrcMatTy->getElementType();
5128
5129 llvm::SmallVector<QualType> DestTypes;
5130 BuildFlattenedTypeList(BaseTy: DestTy, List&: DestTypes);
5131
5132 for (unsigned I = 0, Size = DestTypes.size(); I < Size; ++I) {
5133 if (DestTypes[I]->isUnionType())
5134 return false;
5135 if (!CanPerformScalarCast(SrcTy, DestTy: DestTypes[I]))
5136 return false;
5137 }
5138 return true;
5139}
5140
5141// Can we perform an HLSL Elementwise cast?
5142bool SemaHLSL::CanPerformElementwiseCast(Expr *Src, QualType DestTy) {
5143
5144 // Don't handle casts where LHS and RHS are any combination of scalar/vector
5145 // There must be an aggregate somewhere
5146 QualType SrcTy = Src->getType();
5147 if (SrcTy->isScalarType()) // always a splat and this cast doesn't handle that
5148 return false;
5149
5150 if (SrcTy->isVectorType() &&
5151 (DestTy->isScalarType() || DestTy->isVectorType()))
5152 return false;
5153
5154 if (SrcTy->isConstantMatrixType() &&
5155 (DestTy->isScalarType() || DestTy->isConstantMatrixType()))
5156 return false;
5157
5158 llvm::SmallVector<QualType> DestTypes;
5159 BuildFlattenedTypeList(BaseTy: DestTy, List&: DestTypes);
5160 llvm::SmallVector<QualType> SrcTypes;
5161 BuildFlattenedTypeList(BaseTy: SrcTy, List&: SrcTypes);
5162
5163 // Usually the size of SrcTypes must be greater than or equal to the size of
5164 // DestTypes.
5165 if (SrcTypes.size() < DestTypes.size())
5166 return false;
5167
5168 unsigned SrcSize = SrcTypes.size();
5169 unsigned DstSize = DestTypes.size();
5170 unsigned I;
5171 for (I = 0; I < DstSize && I < SrcSize; I++) {
5172 if (SrcTypes[I]->isUnionType() || DestTypes[I]->isUnionType())
5173 return false;
5174 if (!CanPerformScalarCast(SrcTy: SrcTypes[I], DestTy: DestTypes[I])) {
5175 return false;
5176 }
5177 }
5178
5179 // check the rest of the source type for unions.
5180 for (; I < SrcSize; I++) {
5181 if (SrcTypes[I]->isUnionType())
5182 return false;
5183 }
5184 return true;
5185}
5186
5187ExprResult SemaHLSL::ActOnOutParamExpr(ParmVarDecl *Param, Expr *Arg) {
5188 assert(Param->hasAttr<HLSLParamModifierAttr>() &&
5189 "We should not get here without a parameter modifier expression");
5190 const auto *Attr = Param->getAttr<HLSLParamModifierAttr>();
5191 if (Attr->getABI() == ParameterABI::Ordinary)
5192 return ExprResult(Arg);
5193
5194 bool IsInOut = Attr->getABI() == ParameterABI::HLSLInOut;
5195 if (!Arg->isLValue()) {
5196 SemaRef.Diag(Loc: Arg->getBeginLoc(), DiagID: diag::error_hlsl_inout_lvalue)
5197 << Arg << (IsInOut ? 1 : 0);
5198 return ExprError();
5199 }
5200
5201 ASTContext &Ctx = SemaRef.getASTContext();
5202
5203 QualType Ty = Param->getType().getNonLValueExprType(Context: Ctx);
5204
5205 // HLSL allows implicit conversions from scalars to vectors, but not the
5206 // inverse, so we need to disallow `inout` with scalar->vector or
5207 // scalar->matrix conversions.
5208 if (Arg->getType()->isScalarType() != Ty->isScalarType()) {
5209 SemaRef.Diag(Loc: Arg->getBeginLoc(), DiagID: diag::error_hlsl_inout_scalar_extension)
5210 << Arg << (IsInOut ? 1 : 0);
5211 return ExprError();
5212 }
5213
5214 auto *ArgOpV = new (Ctx) OpaqueValueExpr(Param->getBeginLoc(), Arg->getType(),
5215 VK_LValue, OK_Ordinary, Arg);
5216
5217 // Parameters are initialized via copy initialization. This allows for
5218 // overload resolution of argument constructors.
5219 InitializedEntity Entity =
5220 InitializedEntity::InitializeParameter(Context&: Ctx, Type: Ty, Consumed: false);
5221 ExprResult Res =
5222 SemaRef.PerformCopyInitialization(Entity, EqualLoc: Param->getBeginLoc(), Init: ArgOpV);
5223 if (Res.isInvalid())
5224 return ExprError();
5225 Expr *Base = Res.get();
5226 // After the cast, drop the reference type when creating the exprs.
5227 Ty = Ty.getNonLValueExprType(Context: Ctx);
5228 auto *OpV = new (Ctx)
5229 OpaqueValueExpr(Param->getBeginLoc(), Ty, VK_LValue, OK_Ordinary, Base);
5230
5231 // Writebacks are performed with `=` binary operator, which allows for
5232 // overload resolution on writeback result expressions.
5233 Res = SemaRef.ActOnBinOp(S: SemaRef.getCurScope(), TokLoc: Arg->getBeginLoc(),
5234 Kind: tok::equal, LHSExpr: ArgOpV, RHSExpr: OpV);
5235
5236 if (Res.isInvalid())
5237 return ExprError();
5238 Expr *Writeback = Res.get();
5239 auto *OutExpr =
5240 HLSLOutArgExpr::Create(C: Ctx, Ty, Base: ArgOpV, OpV, WB: Writeback, IsInOut);
5241
5242 return ExprResult(OutExpr);
5243}
5244
5245QualType SemaHLSL::getInoutParameterType(QualType Ty) {
5246 // If HLSL gains support for references, all the cites that use this will need
5247 // to be updated with semantic checking to produce errors for
5248 // pointers/references.
5249 assert(!Ty->isReferenceType() &&
5250 "Pointer and reference types cannot be inout or out parameters");
5251 Ty = SemaRef.getASTContext().getLValueReferenceType(T: Ty);
5252 Ty.addRestrict();
5253 return Ty;
5254}
5255
5256// Returns true if the type has a non-empty constant buffer layout (if it is
5257// scalar, vector or matrix, or if it contains any of these.
5258static bool hasConstantBufferLayout(QualType QT) {
5259 const Type *Ty = QT->getUnqualifiedDesugaredType();
5260 if (Ty->isScalarType() || Ty->isVectorType() || Ty->isMatrixType())
5261 return true;
5262
5263 if (Ty->isHLSLResourceRecord() || Ty->isHLSLResourceRecordArray())
5264 return false;
5265
5266 if (const auto *RD = Ty->getAsCXXRecordDecl()) {
5267 for (const auto *FD : RD->fields()) {
5268 if (hasConstantBufferLayout(QT: FD->getType()))
5269 return true;
5270 }
5271 assert(RD->getNumBases() <= 1 &&
5272 "HLSL doesn't support multiple inheritance");
5273 return RD->getNumBases()
5274 ? hasConstantBufferLayout(QT: RD->bases_begin()->getType())
5275 : false;
5276 }
5277
5278 if (const auto *AT = dyn_cast<ArrayType>(Val: Ty)) {
5279 if (const auto *CAT = dyn_cast<ConstantArrayType>(Val: AT))
5280 if (isZeroSizedArray(CAT))
5281 return false;
5282 return hasConstantBufferLayout(QT: AT->getElementType());
5283 }
5284
5285 return false;
5286}
5287
5288static bool IsDefaultBufferConstantDecl(const ASTContext &Ctx, VarDecl *VD) {
5289 bool IsVulkan =
5290 Ctx.getTargetInfo().getTriple().getOS() == llvm::Triple::Vulkan;
5291 bool IsVKPushConstant = IsVulkan && VD->hasAttr<HLSLVkPushConstantAttr>();
5292 QualType QT = VD->getType();
5293 return VD->getDeclContext()->isTranslationUnit() &&
5294 QT.getAddressSpace() == LangAS::Default &&
5295 VD->getStorageClass() != SC_Static &&
5296 !VD->hasAttr<HLSLVkConstantIdAttr>() && !IsVKPushConstant &&
5297 hasConstantBufferLayout(QT);
5298}
5299
5300void SemaHLSL::deduceAddressSpace(VarDecl *Decl) {
5301 // The variable already has an address space (groupshared for ex).
5302 if (Decl->getType().hasAddressSpace())
5303 return;
5304
5305 if (Decl->getType()->isDependentType())
5306 return;
5307
5308 QualType Type = Decl->getType();
5309
5310 if (Decl->hasAttr<HLSLVkExtBuiltinInputAttr>()) {
5311 LangAS ImplAS = LangAS::hlsl_input;
5312 Type = SemaRef.getASTContext().getAddrSpaceQualType(T: Type, AddressSpace: ImplAS);
5313 Decl->setType(Type);
5314 return;
5315 }
5316
5317 if (Decl->hasAttr<HLSLVkExtBuiltinOutputAttr>()) {
5318 LangAS ImplAS = LangAS::hlsl_output;
5319 Type = SemaRef.getASTContext().getAddrSpaceQualType(T: Type, AddressSpace: ImplAS);
5320 Decl->setType(Type);
5321
5322 // HLSL uses `static` differently than C++. For BuiltIn output, the static
5323 // does not imply private to the module scope.
5324 // Marking it as external to reflect the semantic this attribute brings.
5325 // See https://github.com/microsoft/hlsl-specs/issues/350
5326 Decl->setStorageClass(SC_Extern);
5327 return;
5328 }
5329
5330 bool IsVulkan = getASTContext().getTargetInfo().getTriple().getOS() ==
5331 llvm::Triple::Vulkan;
5332 if (IsVulkan && Decl->hasAttr<HLSLVkPushConstantAttr>()) {
5333 if (HasDeclaredAPushConstant)
5334 SemaRef.Diag(Loc: Decl->getLocation(), DiagID: diag::err_hlsl_push_constant_unique);
5335
5336 LangAS ImplAS = LangAS::hlsl_push_constant;
5337 Type = SemaRef.getASTContext().getAddrSpaceQualType(T: Type, AddressSpace: ImplAS);
5338 Decl->setType(Type);
5339 HasDeclaredAPushConstant = true;
5340 return;
5341 }
5342
5343 if (Type->isSamplerT() || Type->isVoidType())
5344 return;
5345
5346 // Resource handles.
5347 if (Type->isHLSLResourceRecord() || Type->isHLSLResourceRecordArray())
5348 return;
5349
5350 // Only static globals belong to the Private address space.
5351 // Non-static globals belongs to the cbuffer.
5352 if (Decl->getStorageClass() != SC_Static && !Decl->isStaticDataMember())
5353 return;
5354
5355 LangAS ImplAS = LangAS::hlsl_private;
5356 Type = SemaRef.getASTContext().getAddrSpaceQualType(T: Type, AddressSpace: ImplAS);
5357 Decl->setType(Type);
5358}
5359
5360namespace {
5361
5362// Helper class for assigning bindings to resources declared within a struct.
5363// It keeps track of all binding attributes declared on a struct instance, and
5364// the offsets for each register type that have been assigned so far.
5365// Handles both explicit and implicit bindings.
5366class StructBindingContext {
5367 // Bindings and offsets per register type. We only need to support four
5368 // register types - SRV (u), UAV (t), CBuffer (c), and Sampler (s).
5369 HLSLResourceBindingAttr *RegBindingsAttrs[4];
5370 unsigned RegBindingOffset[4];
5371
5372 // Make sure the RegisterType values are what we expect
5373 static_assert(static_cast<unsigned>(RegisterType::SRV) == 0 &&
5374 static_cast<unsigned>(RegisterType::UAV) == 1 &&
5375 static_cast<unsigned>(RegisterType::CBuffer) == 2 &&
5376 static_cast<unsigned>(RegisterType::Sampler) == 3,
5377 "unexpected register type values");
5378
5379 // Vulkan binding attribute does not vary by register type.
5380 HLSLVkBindingAttr *VkBindingAttr;
5381 unsigned VkBindingOffset;
5382
5383public:
5384 // Constructor: gather all binding attributes on a struct instance and
5385 // initialize offsets.
5386 StructBindingContext(VarDecl *VD) {
5387 for (unsigned i = 0; i < 4; ++i) {
5388 RegBindingsAttrs[i] = nullptr;
5389 RegBindingOffset[i] = 0;
5390 }
5391 VkBindingAttr = nullptr;
5392 VkBindingOffset = 0;
5393
5394 ASTContext &AST = VD->getASTContext();
5395 bool IsSpirv = AST.getTargetInfo().getTriple().isSPIRV();
5396
5397 for (Attr *A : VD->attrs()) {
5398 if (auto *RBA = dyn_cast<HLSLResourceBindingAttr>(Val: A)) {
5399 RegisterType RegType = RBA->getRegisterType();
5400 unsigned RegTypeIdx = static_cast<unsigned>(RegType);
5401 // Ignore unsupported register annotations, such as 'c' or 'i'.
5402 if (RegTypeIdx < 4)
5403 RegBindingsAttrs[RegTypeIdx] = RBA;
5404 continue;
5405 }
5406 // Gather the Vulkan binding attributes only if the target is SPIR-V.
5407 if (IsSpirv) {
5408 if (auto *VBA = dyn_cast<HLSLVkBindingAttr>(Val: A))
5409 VkBindingAttr = VBA;
5410 }
5411 }
5412 }
5413
5414 // Creates a binding attribute for a resource based on the gathered attributes
5415 // and the required register type and range.
5416 Attr *createBindingAttr(SemaHLSL &S, ASTContext &AST, RegisterType RegType,
5417 unsigned Range, bool HasCounter) {
5418 assert(static_cast<unsigned>(RegType) < 4 && "unexpected register type");
5419
5420 if (VkBindingAttr) {
5421 unsigned Offset = VkBindingOffset;
5422 VkBindingOffset += Range;
5423 return HLSLVkBindingAttr::CreateImplicit(
5424 Ctx&: AST, Binding: VkBindingAttr->getBinding() + Offset, Set: VkBindingAttr->getSet(),
5425 Range: VkBindingAttr->getRange());
5426 }
5427
5428 HLSLResourceBindingAttr *RBA =
5429 RegBindingsAttrs[static_cast<unsigned>(RegType)];
5430 HLSLResourceBindingAttr *NewAttr = nullptr;
5431
5432 if (RBA && RBA->hasRegisterSlot()) {
5433 // Explicit binding - create a new attribute with offseted slot number
5434 // based on the required register type.
5435 unsigned Offset = RegBindingOffset[static_cast<unsigned>(RegType)];
5436 RegBindingOffset[static_cast<unsigned>(RegType)] += Range;
5437
5438 unsigned NewSlotNumber = RBA->getSlotNumber() + Offset;
5439 StringRef NewSlotNumberStr =
5440 createRegisterString(AST, RegType: RBA->getRegisterType(), N: NewSlotNumber);
5441 NewAttr = HLSLResourceBindingAttr::CreateImplicit(
5442 Ctx&: AST, Slot: NewSlotNumberStr, Space: RBA->getSpace(), Range: RBA->getRange());
5443 NewAttr->setBinding(RT: RegType, SlotNum: NewSlotNumber, SpaceNum: RBA->getSpaceNumber());
5444 } else {
5445 // No binding attribute or space-only binding - create a binding
5446 // attribute for implicit binding.
5447 NewAttr = HLSLResourceBindingAttr::CreateImplicit(Ctx&: AST, Slot: "", Space: "0", Range: {});
5448 NewAttr->setBinding(RT: RegType, SlotNum: std::nullopt,
5449 SpaceNum: RBA ? RBA->getSpaceNumber() : 0);
5450 NewAttr->setImplicitBindingOrderID(S.getNextImplicitBindingOrderID());
5451 }
5452 if (HasCounter)
5453 NewAttr->setImplicitCounterBindingOrderID(
5454 S.getNextImplicitBindingOrderID());
5455 return NewAttr;
5456 }
5457};
5458
5459// Creates a global variable declaration for a resource field embedded in a
5460// struct, assigns it a binding, initializes it, and associates it with the
5461// struct declaration via an HLSLAssociatedResourceDeclAttr.
5462static void createGlobalResourceDeclForStruct(
5463 Sema &S, VarDecl *ParentVD, SourceLocation Loc, IdentifierInfo *Id,
5464 QualType ResTy, StructBindingContext &BindingCtx) {
5465 assert(isResourceRecordTypeOrArrayOf(ResTy) &&
5466 "expected resource type or array of resources");
5467
5468 DeclContext *DC = ParentVD->getNonTransparentDeclContext();
5469 assert(DC->isTranslationUnit() && "expected translation unit decl context");
5470
5471 ASTContext &AST = S.getASTContext();
5472 VarDecl *ResDecl =
5473 VarDecl::Create(C&: AST, DC, StartLoc: Loc, IdLoc: Loc, Id, T: ResTy, TInfo: nullptr, S: SC_None);
5474
5475 unsigned Range = 1;
5476 const Type *SingleResTy = ResTy.getTypePtr()->getUnqualifiedDesugaredType();
5477 while (const auto *AT = dyn_cast<ArrayType>(Val: SingleResTy)) {
5478 const auto *CAT = dyn_cast<ConstantArrayType>(Val: AT);
5479 Range = CAT ? (Range * CAT->getSize().getZExtValue()) : 0;
5480 SingleResTy =
5481 AT->getArrayElementTypeNoTypeQual()->getUnqualifiedDesugaredType();
5482 }
5483 const HLSLAttributedResourceType *ResHandleTy =
5484 HLSLAttributedResourceType::findHandleTypeOnResource(RT: SingleResTy);
5485
5486 // Add a binding attribute to the global resource declaration.
5487 bool HasCounter = hasCounterHandle(RD: SingleResTy->getAsCXXRecordDecl());
5488 Attr *BindingAttr = BindingCtx.createBindingAttr(
5489 S&: S.HLSL(), AST, RegType: getRegisterType(ResTy: ResHandleTy), Range, HasCounter);
5490 ResDecl->addAttr(A: BindingAttr);
5491 ResDecl->addAttr(A: InternalLinkageAttr::CreateImplicit(Ctx&: AST));
5492 ResDecl->setImplicit();
5493
5494 if (Range == 1)
5495 S.HLSL().initGlobalResourceDecl(VD: ResDecl);
5496 else
5497 S.HLSL().initGlobalResourceArrayDecl(VD: ResDecl);
5498
5499 ParentVD->addAttr(
5500 A: HLSLAssociatedResourceDeclAttr::CreateImplicit(Ctx&: AST, ResDecl));
5501 DC->addDecl(D: ResDecl);
5502
5503 DeclGroupRef DG(ResDecl);
5504 S.Consumer.HandleTopLevelDecl(D: DG);
5505}
5506
5507static void handleArrayOfStructWithResources(
5508 Sema &S, VarDecl *ParentVD, const ConstantArrayType *CAT,
5509 EmbeddedResourceNameBuilder &NameBuilder, StructBindingContext &BindingCtx);
5510
5511// Scans base and all fields of a struct/class type to find all embedded
5512// resources or resource arrays. Creates a global variable for each resource
5513// found.
5514static void handleStructWithResources(Sema &S, VarDecl *ParentVD,
5515 const CXXRecordDecl *RD,
5516 EmbeddedResourceNameBuilder &NameBuilder,
5517 StructBindingContext &BindingCtx) {
5518
5519 // Scan the base classes.
5520 assert(RD->getNumBases() <= 1 && "HLSL doesn't support multiple inheritance");
5521 const auto *BasesIt = RD->bases_begin();
5522 if (BasesIt != RD->bases_end()) {
5523 QualType QT = BasesIt->getType();
5524 if (QT->isHLSLIntangibleType()) {
5525 CXXRecordDecl *BaseRD = QT->getAsCXXRecordDecl();
5526 NameBuilder.pushBaseName(N: BaseRD->getName());
5527 handleStructWithResources(S, ParentVD, RD: BaseRD, NameBuilder, BindingCtx);
5528 NameBuilder.pop();
5529 }
5530 }
5531 // Process this class fields.
5532 for (const FieldDecl *FD : RD->fields()) {
5533 QualType FDTy = FD->getType().getCanonicalType();
5534 if (!FDTy->isHLSLIntangibleType())
5535 continue;
5536
5537 NameBuilder.pushName(N: FD->getName());
5538
5539 if (isResourceRecordTypeOrArrayOf(Ty: FDTy)) {
5540 IdentifierInfo *II = NameBuilder.getNameAsIdentifier(AST&: S.getASTContext());
5541 createGlobalResourceDeclForStruct(S, ParentVD, Loc: FD->getLocation(), Id: II,
5542 ResTy: FDTy, BindingCtx);
5543 } else if (const auto *RD = FDTy->getAsCXXRecordDecl()) {
5544 handleStructWithResources(S, ParentVD, RD, NameBuilder, BindingCtx);
5545
5546 } else if (const auto *ArrayTy = dyn_cast<ConstantArrayType>(Val&: FDTy)) {
5547 assert(!FDTy->isHLSLResourceRecordArray() &&
5548 "resource arrays should have been already handled");
5549 handleArrayOfStructWithResources(S, ParentVD, CAT: ArrayTy, NameBuilder,
5550 BindingCtx);
5551 }
5552 NameBuilder.pop();
5553 }
5554}
5555
5556// Processes array of structs with resources.
5557static void
5558handleArrayOfStructWithResources(Sema &S, VarDecl *ParentVD,
5559 const ConstantArrayType *CAT,
5560 EmbeddedResourceNameBuilder &NameBuilder,
5561 StructBindingContext &BindingCtx) {
5562
5563 QualType ElementTy = CAT->getElementType().getCanonicalType();
5564 assert(ElementTy->isHLSLIntangibleType() && "Expected HLSL intangible type");
5565
5566 const ConstantArrayType *SubCAT = dyn_cast<ConstantArrayType>(Val&: ElementTy);
5567 const CXXRecordDecl *ElementRD = ElementTy->getAsCXXRecordDecl();
5568
5569 if (!SubCAT && !ElementRD)
5570 return;
5571
5572 for (unsigned I = 0, E = CAT->getSize().getZExtValue(); I < E; ++I) {
5573 NameBuilder.pushArrayIndex(Index: I);
5574 if (ElementRD)
5575 handleStructWithResources(S, ParentVD, RD: ElementRD, NameBuilder,
5576 BindingCtx);
5577 else
5578 handleArrayOfStructWithResources(S, ParentVD, CAT: SubCAT, NameBuilder,
5579 BindingCtx);
5580 NameBuilder.pop();
5581 }
5582}
5583
5584} // namespace
5585
5586// Scans all fields of a user-defined struct (or array of structs)
5587// to find all embedded resources or resource arrays. For each resource
5588// a global variable of the resource type is created and associated
5589// with the parent declaration (VD) through a HLSLAssociatedResourceDeclAttr
5590// attribute.
5591void SemaHLSL::handleGlobalStructOrArrayOfWithResources(VarDecl *VD) {
5592 EmbeddedResourceNameBuilder NameBuilder(VD->getName());
5593 StructBindingContext BindingCtx(VD);
5594
5595 const Type *VDTy = VD->getType().getTypePtr();
5596 assert(VDTy->isHLSLIntangibleType() && !isResourceRecordTypeOrArrayOf(VD) &&
5597 "Expected non-resource struct or array type");
5598
5599 if (const CXXRecordDecl *RD = VDTy->getAsCXXRecordDecl()) {
5600 handleStructWithResources(S&: SemaRef, ParentVD: VD, RD, NameBuilder, BindingCtx);
5601 return;
5602 }
5603
5604 if (const auto *CAT = dyn_cast<ConstantArrayType>(Val: VDTy)) {
5605 handleArrayOfStructWithResources(S&: SemaRef, ParentVD: VD, CAT, NameBuilder, BindingCtx);
5606 return;
5607 }
5608}
5609
5610void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
5611 if (VD->hasGlobalStorage()) {
5612 // make sure the declaration has a complete type
5613 if (SemaRef.RequireCompleteType(
5614 Loc: VD->getLocation(),
5615 T: SemaRef.getASTContext().getBaseElementType(QT: VD->getType()),
5616 DiagID: diag::err_typecheck_decl_incomplete_type)) {
5617 VD->setInvalidDecl();
5618 deduceAddressSpace(Decl: VD);
5619 return;
5620 }
5621
5622 // Global variables outside a cbuffer block that are not a resource, static,
5623 // groupshared, or an empty array or struct belong to the default constant
5624 // buffer $Globals (to be created at the end of the translation unit).
5625 if (IsDefaultBufferConstantDecl(Ctx: getASTContext(), VD)) {
5626 // update address space to hlsl_constant
5627 QualType NewTy = getASTContext().getAddrSpaceQualType(
5628 T: VD->getType(), AddressSpace: LangAS::hlsl_constant);
5629 VD->setType(NewTy);
5630 DefaultCBufferDecls.push_back(Elt: VD);
5631 }
5632
5633 // find all resources bindings on decl
5634 if (VD->getType()->isHLSLIntangibleType())
5635 collectResourceBindingsOnVarDecl(D: VD);
5636
5637 if (VD->hasAttr<HLSLVkConstantIdAttr>())
5638 VD->setStorageClass(StorageClass::SC_Static);
5639
5640 if (isResourceRecordTypeOrArrayOf(VD) &&
5641 VD->getStorageClass() != SC_Static) {
5642 // Add internal linkage attribute to non-static resource variables. The
5643 // global externally visible storage is accessed through the handle, which
5644 // is a member. The variable itself is not externally visible.
5645 VD->addAttr(A: InternalLinkageAttr::CreateImplicit(Ctx&: getASTContext()));
5646 }
5647
5648 // process explicit bindings
5649 processExplicitBindingsOnDecl(D: VD);
5650
5651 // Add implicit binding attribute to non-static resource arrays.
5652 if (VD->getType()->isHLSLResourceRecordArray() &&
5653 VD->getStorageClass() != SC_Static) {
5654 // If the resource array does not have an explicit binding attribute,
5655 // create an implicit one. It will be used to transfer implicit binding
5656 // order_ID to codegen.
5657 ResourceBindingAttrs Binding(VD);
5658 if (!Binding.isExplicit()) {
5659 uint32_t OrderID = getNextImplicitBindingOrderID();
5660 if (Binding.hasBinding())
5661 Binding.setImplicitOrderID(OrderID);
5662 else {
5663 addImplicitBindingAttrToDecl(
5664 S&: SemaRef, D: VD, RT: getRegisterType(ResTy: getResourceArrayHandleType(VD)),
5665 ImplicitBindingOrderID: OrderID);
5666 // Re-create the binding object to pick up the new attribute.
5667 Binding = ResourceBindingAttrs(VD);
5668 }
5669 }
5670
5671 // Get to the base type of a potentially multi-dimensional array.
5672 QualType Ty = getASTContext().getBaseElementType(QT: VD->getType());
5673
5674 const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
5675 if (hasCounterHandle(RD)) {
5676 if (!Binding.hasCounterImplicitOrderID()) {
5677 uint32_t OrderID = getNextImplicitBindingOrderID();
5678 Binding.setCounterImplicitOrderID(OrderID);
5679 }
5680 }
5681 }
5682
5683 // Process resources in user-defined structs, or arrays of such structs.
5684 const Type *VDTy = VD->getType().getTypePtr();
5685 if (VD->getStorageClass() != SC_Static && VDTy->isHLSLIntangibleType() &&
5686 !isResourceRecordTypeOrArrayOf(VD))
5687 handleGlobalStructOrArrayOfWithResources(VD);
5688
5689 // Mark groupshared variables as extern so they will have
5690 // external storage and won't be default initialized
5691 if (VD->hasAttr<HLSLGroupSharedAddressSpaceAttr>())
5692 VD->setStorageClass(StorageClass::SC_Extern);
5693 }
5694
5695 deduceAddressSpace(Decl: VD);
5696}
5697
5698bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
5699 assert(VD->getType()->isHLSLResourceRecord() &&
5700 "expected resource record type");
5701
5702 ASTContext &AST = SemaRef.getASTContext();
5703 uint64_t UIntTySize = AST.getTypeSize(T: AST.UnsignedIntTy);
5704 uint64_t IntTySize = AST.getTypeSize(T: AST.IntTy);
5705
5706 // Gather resource binding attributes.
5707 ResourceBindingAttrs Binding(VD);
5708
5709 // Find correct initialization method and create its arguments.
5710 QualType ResourceTy = VD->getType();
5711 CXXRecordDecl *ResourceDecl = ResourceTy->getAsCXXRecordDecl();
5712 CXXMethodDecl *CreateMethod = nullptr;
5713 llvm::SmallVector<Expr *> Args;
5714
5715 bool HasCounter = hasCounterHandle(RD: ResourceDecl);
5716 const char *CreateMethodName;
5717 if (Binding.isExplicit())
5718 CreateMethodName = HasCounter ? "__createFromBindingWithImplicitCounter"
5719 : "__createFromBinding";
5720 else
5721 CreateMethodName = HasCounter
5722 ? "__createFromImplicitBindingWithImplicitCounter"
5723 : "__createFromImplicitBinding";
5724
5725 CreateMethod =
5726 lookupMethod(S&: SemaRef, RecordDecl: ResourceDecl, Name: CreateMethodName, Loc: VD->getLocation());
5727
5728 if (!CreateMethod) {
5729 // This can happen if someone creates a struct that looks like an HLSL
5730 // resource record but does not have the required static create method.
5731 // No binding will be generated for it.
5732 assert(!ResourceDecl->isImplicit() &&
5733 "create method lookup should always succeed for built-in resource "
5734 "records");
5735 return false;
5736 }
5737
5738 if (Binding.isExplicit()) {
5739 IntegerLiteral *RegSlot =
5740 IntegerLiteral::Create(C: AST, V: llvm::APInt(UIntTySize, Binding.getSlot()),
5741 type: AST.UnsignedIntTy, l: SourceLocation());
5742 Args.push_back(Elt: RegSlot);
5743 } else {
5744 uint32_t OrderID = (Binding.hasImplicitOrderID())
5745 ? Binding.getImplicitOrderID()
5746 : getNextImplicitBindingOrderID();
5747 IntegerLiteral *OrderId =
5748 IntegerLiteral::Create(C: AST, V: llvm::APInt(UIntTySize, OrderID),
5749 type: AST.UnsignedIntTy, l: SourceLocation());
5750 Args.push_back(Elt: OrderId);
5751 }
5752
5753 IntegerLiteral *Space =
5754 IntegerLiteral::Create(C: AST, V: llvm::APInt(UIntTySize, Binding.getSpace()),
5755 type: AST.UnsignedIntTy, l: SourceLocation());
5756 Args.push_back(Elt: Space);
5757
5758 IntegerLiteral *RangeSize = IntegerLiteral::Create(
5759 C: AST, V: llvm::APInt(IntTySize, 1), type: AST.IntTy, l: SourceLocation());
5760 Args.push_back(Elt: RangeSize);
5761
5762 IntegerLiteral *Index = IntegerLiteral::Create(
5763 C: AST, V: llvm::APInt(UIntTySize, 0), type: AST.UnsignedIntTy, l: SourceLocation());
5764 Args.push_back(Elt: Index);
5765
5766 StringRef VarName = VD->getName();
5767 StringLiteral *Name = StringLiteral::Create(
5768 Ctx: AST, Str: VarName, Kind: StringLiteralKind::Ordinary, Pascal: false,
5769 Ty: AST.getStringLiteralArrayType(EltTy: AST.CharTy.withConst(), Length: VarName.size()),
5770 Locs: SourceLocation());
5771 ImplicitCastExpr *NameCast = ImplicitCastExpr::Create(
5772 Context: AST, T: AST.getPointerType(T: AST.CharTy.withConst()), Kind: CK_ArrayToPointerDecay,
5773 Operand: Name, BasePath: nullptr, Cat: VK_PRValue, FPO: FPOptionsOverride());
5774 Args.push_back(Elt: NameCast);
5775
5776 if (HasCounter) {
5777 // Will this be in the correct order?
5778 uint32_t CounterOrderID = getNextImplicitBindingOrderID();
5779 IntegerLiteral *CounterId =
5780 IntegerLiteral::Create(C: AST, V: llvm::APInt(UIntTySize, CounterOrderID),
5781 type: AST.UnsignedIntTy, l: SourceLocation());
5782 Args.push_back(Elt: CounterId);
5783 }
5784
5785 // Make sure the create method template is instantiated and emitted.
5786 if (!CreateMethod->isDefined() && CreateMethod->isTemplateInstantiation())
5787 SemaRef.InstantiateFunctionDefinition(PointOfInstantiation: VD->getLocation(), Function: CreateMethod,
5788 Recursive: true);
5789
5790 // Create CallExpr with a call to the static method and set it as the decl
5791 // initialization.
5792 DeclRefExpr *DRE = DeclRefExpr::Create(
5793 Context: AST, QualifierLoc: NestedNameSpecifierLoc(), TemplateKWLoc: SourceLocation(), D: CreateMethod, RefersToEnclosingVariableOrCapture: false,
5794 NameInfo: CreateMethod->getNameInfo(), T: CreateMethod->getType(), VK: VK_PRValue);
5795
5796 auto *ImpCast = ImplicitCastExpr::Create(
5797 Context: AST, T: AST.getPointerType(T: CreateMethod->getType()),
5798 Kind: CK_FunctionToPointerDecay, Operand: DRE, BasePath: nullptr, Cat: VK_PRValue, FPO: FPOptionsOverride());
5799
5800 CallExpr *InitExpr =
5801 CallExpr::Create(Ctx: AST, Fn: ImpCast, Args, Ty: ResourceTy, VK: VK_PRValue,
5802 RParenLoc: SourceLocation(), FPFeatures: FPOptionsOverride());
5803 VD->setInit(InitExpr);
5804 VD->setInitStyle(VarDecl::CallInit);
5805 SemaRef.CheckCompleteVariableDeclaration(VD);
5806 return true;
5807}
5808
5809bool SemaHLSL::initGlobalResourceArrayDecl(VarDecl *VD) {
5810 assert(VD->getType()->isHLSLResourceRecordArray() &&
5811 "expected array of resource records");
5812
5813 // Individual resources in a resource array are not initialized here. They
5814 // are initialized later on during codegen when the individual resources are
5815 // accessed. Codegen will emit a call to the resource initialization method
5816 // with the specified array index. We need to make sure though that the method
5817 // for the specific resource type is instantiated, so codegen can emit a call
5818 // to it when the array element is accessed.
5819
5820 // Find correct initialization method based on the resource binding
5821 // information.
5822 ASTContext &AST = SemaRef.getASTContext();
5823 QualType ResElementTy = AST.getBaseElementType(QT: VD->getType());
5824 CXXRecordDecl *ResourceDecl = ResElementTy->getAsCXXRecordDecl();
5825 CXXMethodDecl *CreateMethod = nullptr;
5826
5827 bool HasCounter = hasCounterHandle(RD: ResourceDecl);
5828 ResourceBindingAttrs ResourceAttrs(VD);
5829 if (ResourceAttrs.isExplicit())
5830 // Resource has explicit binding.
5831 CreateMethod =
5832 lookupMethod(S&: SemaRef, RecordDecl: ResourceDecl,
5833 Name: HasCounter ? "__createFromBindingWithImplicitCounter"
5834 : "__createFromBinding",
5835 Loc: VD->getLocation());
5836 else
5837 // Resource has implicit binding.
5838 CreateMethod = lookupMethod(
5839 S&: SemaRef, RecordDecl: ResourceDecl,
5840 Name: HasCounter ? "__createFromImplicitBindingWithImplicitCounter"
5841 : "__createFromImplicitBinding",
5842 Loc: VD->getLocation());
5843
5844 if (!CreateMethod)
5845 return false;
5846
5847 // Make sure the create method template is instantiated and emitted.
5848 if (!CreateMethod->isDefined() && CreateMethod->isTemplateInstantiation())
5849 SemaRef.InstantiateFunctionDefinition(PointOfInstantiation: VD->getLocation(), Function: CreateMethod,
5850 Recursive: true);
5851 return true;
5852}
5853
5854// Returns true if the initialization has been handled.
5855// Returns false to use default initialization.
5856bool SemaHLSL::ActOnUninitializedVarDecl(VarDecl *VD) {
5857 // Objects in the hlsl_constant address space are initialized
5858 // externally, so don't synthesize an implicit initializer.
5859 if (VD->getType().getAddressSpace() == LangAS::hlsl_constant)
5860 return true;
5861
5862 if (VD->hasGlobalStorage() && VD->getStorageClass() != SC_Static) {
5863 const Type *Ty = VD->getType().getTypePtr();
5864 if (Ty->isHLSLResourceRecord() && initGlobalResourceDecl(VD))
5865 return true;
5866 if (Ty->isHLSLResourceRecordArray() && initGlobalResourceArrayDecl(VD))
5867 return true;
5868 }
5869
5870 // User-defined structs/classes do not have constructors.
5871 // When declared at a global scope, they are part of the constant buffer
5872 // and should not be initialized by the compiler.
5873 // When declared at a local scope, they are not initialized.
5874 // Also applies to arrays of user-defined structs/classes.
5875 const Type *Ty = VD->getType()->getUnqualifiedDesugaredType();
5876 while (Ty->isArrayType())
5877 Ty = Ty->getArrayElementTypeNoTypeQual()->getUnqualifiedDesugaredType();
5878 if (CXXRecordDecl *RD = Ty->getAsCXXRecordDecl())
5879 return !RD->isHLSLBuiltinRecord();
5880
5881 return false;
5882}
5883
5884std::optional<const DeclBindingInfo *> SemaHLSL::inferGlobalBinding(Expr *E) {
5885 if (auto *Ternary = dyn_cast<ConditionalOperator>(Val: E)) {
5886 auto TrueInfo = inferGlobalBinding(E: Ternary->getTrueExpr());
5887 auto FalseInfo = inferGlobalBinding(E: Ternary->getFalseExpr());
5888 if (!TrueInfo || !FalseInfo)
5889 return std::nullopt;
5890 if (*TrueInfo != *FalseInfo)
5891 return std::nullopt;
5892 return TrueInfo;
5893 }
5894
5895 if (auto *ASE = dyn_cast<ArraySubscriptExpr>(Val: E))
5896 E = ASE->getBase()->IgnoreParenImpCasts();
5897
5898 if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Val: E->IgnoreParens()))
5899 if (VarDecl *VD = dyn_cast<VarDecl>(Val: DRE->getDecl())) {
5900 const Type *Ty = VD->getType()->getUnqualifiedDesugaredType();
5901 if (Ty->isArrayType())
5902 Ty = Ty->getArrayElementTypeNoTypeQual();
5903
5904 if (const auto *AttrResType =
5905 HLSLAttributedResourceType::findHandleTypeOnResource(RT: Ty)) {
5906 ResourceClass RC = AttrResType->getAttrs().ResourceClass;
5907 return Bindings.getDeclBindingInfo(VD, ResClass: RC);
5908 }
5909 }
5910
5911 return nullptr;
5912}
5913
5914void SemaHLSL::trackLocalResource(VarDecl *VD, Expr *E) {
5915 std::optional<const DeclBindingInfo *> ExprBinding = inferGlobalBinding(E);
5916 if (!ExprBinding) {
5917 SemaRef.Diag(Loc: E->getBeginLoc(),
5918 DiagID: diag::warn_hlsl_assigning_local_resource_is_not_unique)
5919 << E << VD;
5920 return; // Expr use multiple resources
5921 }
5922
5923 if (*ExprBinding == nullptr)
5924 return; // No binding could be inferred to track, return without error
5925
5926 auto PrevBinding = Assigns.find(Val: VD);
5927 if (PrevBinding == Assigns.end()) {
5928 // No previous binding recorded, simply record the new assignment
5929 Assigns.insert(KV: {VD, *ExprBinding});
5930 return;
5931 }
5932
5933 // Otherwise, warn if the assignment implies different resource bindings
5934 if (*ExprBinding != PrevBinding->second) {
5935 SemaRef.Diag(Loc: E->getBeginLoc(),
5936 DiagID: diag::warn_hlsl_assigning_local_resource_is_not_unique)
5937 << E << VD;
5938 SemaRef.Diag(Loc: VD->getLocation(), DiagID: diag::note_var_declared_here) << VD;
5939 return;
5940 }
5941
5942 return;
5943}
5944
5945bool SemaHLSL::CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr,
5946 Expr *RHSExpr, SourceLocation Loc) {
5947 assert((LHSExpr->getType()->isHLSLResourceRecord() ||
5948 LHSExpr->getType()->isHLSLResourceRecordArray()) &&
5949 "expected LHS to be a resource record or array of resource records");
5950 if (Opc != BO_Assign)
5951 return true;
5952
5953 // If LHS is an array subscript, get the underlying declaration.
5954 Expr *E = LHSExpr;
5955 while (auto *ASE = dyn_cast<ArraySubscriptExpr>(Val: E))
5956 E = ASE->getBase()->IgnoreParenImpCasts();
5957
5958 // Report error if LHS is a non-static resource declared at a global scope.
5959 if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Val: E->IgnoreParens())) {
5960 if (VarDecl *VD = dyn_cast<VarDecl>(Val: DRE->getDecl())) {
5961 if (VD->hasGlobalStorage() && VD->getStorageClass() != SC_Static) {
5962 // assignment to global resource is not allowed
5963 SemaRef.Diag(Loc, DiagID: diag::err_hlsl_assign_to_global_resource) << VD;
5964 SemaRef.Diag(Loc: VD->getLocation(), DiagID: diag::note_var_declared_here) << VD;
5965 return false;
5966 }
5967
5968 trackLocalResource(VD, E: RHSExpr);
5969 }
5970 }
5971 return true;
5972}
5973
5974// Returns true if the given type can have an overload of the given
5975// binary operator.
5976bool SemaHLSL::canHaveOverloadedBinOp(QualType LHSTy, BinaryOperatorKind Opc) {
5977 CXXRecordDecl *RD = LHSTy->getAsCXXRecordDecl();
5978 if (!RD)
5979 return true;
5980 return RD->isHLSLBuiltinRecord() || Opc != BO_Assign;
5981}
5982
5983// Walks though the global variable declaration, collects all resource binding
5984// requirements and adds them to Bindings
5985void SemaHLSL::collectResourceBindingsOnVarDecl(VarDecl *VD) {
5986 assert(VD->hasGlobalStorage() && VD->getType()->isHLSLIntangibleType() &&
5987 "expected global variable that contains HLSL resource");
5988
5989 // Cbuffers and Tbuffers are HLSLBufferDecl types
5990 if (const HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(Val: VD)) {
5991 Bindings.addDeclBindingInfo(VD, ResClass: CBufferOrTBuffer->isCBuffer()
5992 ? ResourceClass::CBuffer
5993 : ResourceClass::SRV);
5994 return;
5995 }
5996
5997 // Unwrap arrays
5998 // FIXME: Calculate array size while unwrapping
5999 const Type *Ty = VD->getType()->getUnqualifiedDesugaredType();
6000 while (Ty->isArrayType()) {
6001 const ArrayType *AT = cast<ArrayType>(Val: Ty);
6002 Ty = AT->getElementType()->getUnqualifiedDesugaredType();
6003 }
6004
6005 // Resource (or array of resources)
6006 if (const HLSLAttributedResourceType *AttrResType =
6007 HLSLAttributedResourceType::findHandleTypeOnResource(RT: Ty)) {
6008 Bindings.addDeclBindingInfo(VD, ResClass: AttrResType->getAttrs().ResourceClass);
6009 return;
6010 }
6011
6012 // User defined record type
6013 if (const RecordType *RT = dyn_cast<RecordType>(Val: Ty))
6014 collectResourceBindingsOnUserRecordDecl(VD, RT);
6015}
6016
6017// Walks though the explicit resource binding attributes on the declaration,
6018// and makes sure there is a resource that matched the binding and updates
6019// DeclBindingInfoLists
6020void SemaHLSL::processExplicitBindingsOnDecl(VarDecl *VD) {
6021 assert(VD->hasGlobalStorage() && "expected global variable");
6022
6023 bool HasBinding = false;
6024 for (Attr *A : VD->attrs()) {
6025 if (isa<HLSLVkBindingAttr>(Val: A)) {
6026 HasBinding = true;
6027 if (auto PA = VD->getAttr<HLSLVkPushConstantAttr>())
6028 Diag(Loc: PA->getLoc(), DiagID: diag::err_hlsl_attr_incompatible) << A << PA;
6029 }
6030
6031 HLSLResourceBindingAttr *RBA = dyn_cast<HLSLResourceBindingAttr>(Val: A);
6032 if (!RBA || !RBA->hasRegisterSlot())
6033 continue;
6034 HasBinding = true;
6035
6036 RegisterType RT = RBA->getRegisterType();
6037 assert(RT != RegisterType::I && "invalid or obsolete register type should "
6038 "never have an attribute created");
6039
6040 if (RT == RegisterType::C) {
6041 if (Bindings.hasBindingInfoForDecl(VD))
6042 SemaRef.Diag(Loc: VD->getLocation(),
6043 DiagID: diag::warn_hlsl_user_defined_type_missing_member)
6044 << static_cast<int>(RT);
6045 continue;
6046 }
6047
6048 // Find DeclBindingInfo for this binding and update it, or report error
6049 // if it does not exist (user type does to contain resources with the
6050 // expected resource class).
6051 ResourceClass RC = getResourceClass(RT);
6052 if (DeclBindingInfo *BI = Bindings.getDeclBindingInfo(VD, ResClass: RC)) {
6053 // update binding info
6054 BI->setBindingAttribute(A: RBA, BT: BindingType::Explicit);
6055 } else {
6056 SemaRef.Diag(Loc: VD->getLocation(),
6057 DiagID: diag::warn_hlsl_user_defined_type_missing_member)
6058 << static_cast<int>(RT);
6059 }
6060 }
6061
6062 if (!HasBinding && isResourceRecordTypeOrArrayOf(VD))
6063 SemaRef.Diag(Loc: VD->getLocation(), DiagID: diag::warn_hlsl_implicit_binding);
6064}
6065namespace {
6066class InitListTransformer {
6067 Sema &S;
6068 ASTContext &Ctx;
6069 QualType InitTy;
6070 QualType *DstIt = nullptr;
6071 Expr **ArgIt = nullptr;
6072 // Is wrapping the destination type iterator required? This is only used for
6073 // incomplete array types where we loop over the destination type since we
6074 // don't know the full number of elements from the declaration.
6075 bool Wrap;
6076
6077 bool castInitializer(Expr *E) {
6078 assert(DstIt && "This should always be something!");
6079 if (DstIt == DestTypes.end()) {
6080 if (!Wrap) {
6081 ArgExprs.push_back(Elt: E);
6082 // This is odd, but it isn't technically a failure due to conversion, we
6083 // handle mismatched counts of arguments differently.
6084 return true;
6085 }
6086 DstIt = DestTypes.begin();
6087 }
6088 InitializedEntity Entity = InitializedEntity::InitializeParameter(
6089 Context&: Ctx, Type: *DstIt, /* Consumed (ObjC) */ Consumed: false);
6090 ExprResult Res = S.PerformCopyInitialization(Entity, EqualLoc: E->getBeginLoc(), Init: E);
6091 if (Res.isInvalid())
6092 return false;
6093 Expr *Init = Res.get();
6094 ArgExprs.push_back(Elt: Init);
6095 DstIt++;
6096 return true;
6097 }
6098
6099 bool buildInitializerListImpl(Expr *E) {
6100 // If this is an initialization list, traverse the sub initializers.
6101 if (auto *Init = dyn_cast<InitListExpr>(Val: E)) {
6102 for (auto *SubInit : Init->inits())
6103 if (!buildInitializerListImpl(E: SubInit))
6104 return false;
6105 return true;
6106 }
6107
6108 // If this is a scalar type, just enqueue the expression.
6109 QualType Ty = E->getType().getDesugaredType(Context: Ctx);
6110
6111 if (Ty->isScalarType() || (Ty->isRecordType() && !Ty->isAggregateType()) ||
6112 Ty->isHLSLAttributedResourceType())
6113 return castInitializer(E);
6114
6115 // If this is an aggregate type and a prvalue, create an xvalue temporary
6116 // so the member accesses will be xvalues. Wrap it in OpaqueExpr to make
6117 // sure codegen will not generate duplicate copies.
6118 if (E->isPRValue() && Ty->isAggregateType()) {
6119 ExprResult TmpExpr = S.TemporaryMaterializationConversion(E);
6120 if (TmpExpr.isInvalid())
6121 return false;
6122 E = TmpExpr.get();
6123 E = new (Ctx) OpaqueValueExpr(E->getBeginLoc(), E->getType(),
6124 E->getValueKind(), E->getObjectKind(), E);
6125 }
6126
6127 if (auto *VecTy = Ty->getAs<VectorType>()) {
6128 uint64_t Size = VecTy->getNumElements();
6129
6130 QualType SizeTy = Ctx.getSizeType();
6131 uint64_t SizeTySize = Ctx.getTypeSize(T: SizeTy);
6132 for (uint64_t I = 0; I < Size; ++I) {
6133 auto *Idx = IntegerLiteral::Create(C: Ctx, V: llvm::APInt(SizeTySize, I),
6134 type: SizeTy, l: SourceLocation());
6135
6136 ExprResult ElExpr = S.CreateBuiltinArraySubscriptExpr(
6137 Base: E, LLoc: E->getBeginLoc(), Idx, RLoc: E->getEndLoc());
6138 if (ElExpr.isInvalid())
6139 return false;
6140 if (!castInitializer(E: ElExpr.get()))
6141 return false;
6142 }
6143 return true;
6144 }
6145 if (auto *MTy = Ty->getAs<ConstantMatrixType>()) {
6146 unsigned Rows = MTy->getNumRows();
6147 unsigned Cols = MTy->getNumColumns();
6148 QualType ElemTy = MTy->getElementType();
6149
6150 for (unsigned R = 0; R < Rows; ++R) {
6151 for (unsigned C = 0; C < Cols; ++C) {
6152 // row index literal
6153 Expr *RowIdx = IntegerLiteral::Create(
6154 C: Ctx, V: llvm::APInt(Ctx.getIntWidth(T: Ctx.IntTy), R), type: Ctx.IntTy,
6155 l: E->getBeginLoc());
6156 // column index literal
6157 Expr *ColIdx = IntegerLiteral::Create(
6158 C: Ctx, V: llvm::APInt(Ctx.getIntWidth(T: Ctx.IntTy), C), type: Ctx.IntTy,
6159 l: E->getBeginLoc());
6160 ExprResult ElExpr = S.CreateBuiltinMatrixSubscriptExpr(
6161 Base: E, RowIdx, ColumnIdx: ColIdx, RBLoc: E->getEndLoc());
6162 if (ElExpr.isInvalid())
6163 return false;
6164 if (!castInitializer(E: ElExpr.get()))
6165 return false;
6166 ElExpr.get()->setType(ElemTy);
6167 }
6168 }
6169 return true;
6170 }
6171
6172 if (auto *ArrTy = dyn_cast<ConstantArrayType>(Val: Ty.getTypePtr())) {
6173 uint64_t Size = ArrTy->getZExtSize();
6174 QualType SizeTy = Ctx.getSizeType();
6175 uint64_t SizeTySize = Ctx.getTypeSize(T: SizeTy);
6176 for (uint64_t I = 0; I < Size; ++I) {
6177 auto *Idx = IntegerLiteral::Create(C: Ctx, V: llvm::APInt(SizeTySize, I),
6178 type: SizeTy, l: SourceLocation());
6179 ExprResult ElExpr = S.CreateBuiltinArraySubscriptExpr(
6180 Base: E, LLoc: E->getBeginLoc(), Idx, RLoc: E->getEndLoc());
6181 if (ElExpr.isInvalid())
6182 return false;
6183 if (!buildInitializerListImpl(E: ElExpr.get()))
6184 return false;
6185 }
6186 return true;
6187 }
6188
6189 if (auto *RD = Ty->getAsCXXRecordDecl()) {
6190 llvm::SmallVector<CXXRecordDecl *> RecordDecls;
6191 RecordDecls.push_back(Elt: RD);
6192 while (RecordDecls.back()->getNumBases()) {
6193 CXXRecordDecl *D = RecordDecls.back();
6194 assert(D->getNumBases() == 1 &&
6195 "HLSL doesn't support multiple inheritance");
6196 RecordDecls.push_back(
6197 Elt: D->bases_begin()->getType()->castAsCXXRecordDecl());
6198 }
6199 while (!RecordDecls.empty()) {
6200 CXXRecordDecl *RD = RecordDecls.pop_back_val();
6201 for (auto *FD : RD->fields()) {
6202 if (FD->isUnnamedBitField())
6203 continue;
6204 DeclAccessPair Found = DeclAccessPair::make(D: FD, AS: FD->getAccess());
6205 DeclarationNameInfo NameInfo(FD->getDeclName(), E->getBeginLoc());
6206 ExprResult Res = S.BuildFieldReferenceExpr(
6207 BaseExpr: E, IsArrow: false, OpLoc: E->getBeginLoc(), SS: CXXScopeSpec(), Field: FD, FoundDecl: Found, MemberNameInfo: NameInfo);
6208 if (Res.isInvalid())
6209 return false;
6210 if (!buildInitializerListImpl(E: Res.get()))
6211 return false;
6212 }
6213 }
6214 }
6215 return true;
6216 }
6217
6218 Expr *generateInitListsImpl(QualType Ty) {
6219 Ty = Ty.getDesugaredType(Context: Ctx);
6220 assert(ArgIt != ArgExprs.end() && "Something is off in iteration!");
6221 if (Ty->isScalarType() || (Ty->isRecordType() && !Ty->isAggregateType()) ||
6222 Ty->isHLSLAttributedResourceType())
6223 return *(ArgIt++);
6224
6225 llvm::SmallVector<Expr *> Inits;
6226 if (Ty->isVectorType() || Ty->isConstantArrayType() ||
6227 Ty->isConstantMatrixType()) {
6228 QualType ElTy;
6229 uint64_t Size = 0;
6230 if (auto *ATy = Ty->getAs<VectorType>()) {
6231 ElTy = ATy->getElementType();
6232 Size = ATy->getNumElements();
6233 } else if (auto *CMTy = Ty->getAs<ConstantMatrixType>()) {
6234 ElTy = CMTy->getElementType();
6235 Size = CMTy->getNumElementsFlattened();
6236 } else {
6237 auto *VTy = cast<ConstantArrayType>(Val: Ty.getTypePtr());
6238 ElTy = VTy->getElementType();
6239 Size = VTy->getZExtSize();
6240 }
6241 for (uint64_t I = 0; I < Size; ++I)
6242 Inits.push_back(Elt: generateInitListsImpl(Ty: ElTy));
6243 }
6244 if (auto *RD = Ty->getAsCXXRecordDecl()) {
6245 llvm::SmallVector<CXXRecordDecl *> RecordDecls;
6246 RecordDecls.push_back(Elt: RD);
6247 while (RecordDecls.back()->getNumBases()) {
6248 CXXRecordDecl *D = RecordDecls.back();
6249 assert(D->getNumBases() == 1 &&
6250 "HLSL doesn't support multiple inheritance");
6251 RecordDecls.push_back(
6252 Elt: D->bases_begin()->getType()->castAsCXXRecordDecl());
6253 }
6254 while (!RecordDecls.empty()) {
6255 CXXRecordDecl *RD = RecordDecls.pop_back_val();
6256 for (auto *FD : RD->fields())
6257 if (!FD->isUnnamedBitField())
6258 Inits.push_back(Elt: generateInitListsImpl(Ty: FD->getType()));
6259 }
6260 }
6261 auto *NewInit =
6262 new (Ctx) InitListExpr(Ctx, Inits.front()->getBeginLoc(), Inits,
6263 Inits.back()->getEndLoc(), /*isExplicit=*/false);
6264 NewInit->setType(Ty);
6265 return NewInit;
6266 }
6267
6268public:
6269 llvm::SmallVector<QualType, 16> DestTypes;
6270 llvm::SmallVector<Expr *, 16> ArgExprs;
6271 InitListTransformer(Sema &SemaRef, const InitializedEntity &Entity)
6272 : S(SemaRef), Ctx(SemaRef.getASTContext()),
6273 Wrap(Entity.getType()->isIncompleteArrayType()) {
6274 InitTy = Entity.getType().getNonReferenceType();
6275 // When we're generating initializer lists for incomplete array types we
6276 // need to wrap around both when building the initializers and when
6277 // generating the final initializer lists.
6278 if (Wrap) {
6279 assert(InitTy->isIncompleteArrayType());
6280 const IncompleteArrayType *IAT = Ctx.getAsIncompleteArrayType(T: InitTy);
6281 InitTy = IAT->getElementType();
6282 }
6283 BuildFlattenedTypeList(BaseTy: InitTy, List&: DestTypes);
6284 DstIt = DestTypes.begin();
6285 }
6286
6287 bool buildInitializerList(Expr *E) { return buildInitializerListImpl(E); }
6288
6289 Expr *generateInitLists() {
6290 assert(!ArgExprs.empty() &&
6291 "Call buildInitializerList to generate argument expressions.");
6292 ArgIt = ArgExprs.begin();
6293 if (!Wrap)
6294 return generateInitListsImpl(Ty: InitTy);
6295 llvm::SmallVector<Expr *> Inits;
6296 while (ArgIt != ArgExprs.end())
6297 Inits.push_back(Elt: generateInitListsImpl(Ty: InitTy));
6298
6299 auto *NewInit =
6300 new (Ctx) InitListExpr(Ctx, Inits.front()->getBeginLoc(), Inits,
6301 Inits.back()->getEndLoc(), /*isExplicit=*/false);
6302 llvm::APInt ArySize(64, Inits.size());
6303 NewInit->setType(Ctx.getConstantArrayType(EltTy: InitTy, ArySize, SizeExpr: nullptr,
6304 ASM: ArraySizeModifier::Normal, IndexTypeQuals: 0));
6305 return NewInit;
6306 }
6307};
6308} // namespace
6309
6310// Recursively detect any incomplete array anywhere in the type graph,
6311// including arrays, struct fields, and base classes.
6312static bool containsIncompleteArrayType(QualType Ty) {
6313 Ty = Ty.getCanonicalType();
6314
6315 // Array types
6316 if (const ArrayType *AT = dyn_cast<ArrayType>(Val&: Ty)) {
6317 if (isa<IncompleteArrayType>(Val: AT))
6318 return true;
6319 return containsIncompleteArrayType(Ty: AT->getElementType());
6320 }
6321
6322 // Record (struct/class) types
6323 if (const auto *RT = Ty->getAs<RecordType>()) {
6324 const RecordDecl *RD = RT->getDecl();
6325
6326 // Walk base classes (for C++ / HLSL structs with inheritance)
6327 if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(Val: RD)) {
6328 for (const CXXBaseSpecifier &Base : CXXRD->bases()) {
6329 if (containsIncompleteArrayType(Ty: Base.getType()))
6330 return true;
6331 }
6332 }
6333
6334 // Walk fields
6335 for (const FieldDecl *F : RD->fields()) {
6336 if (containsIncompleteArrayType(Ty: F->getType()))
6337 return true;
6338 }
6339 }
6340
6341 return false;
6342}
6343
6344bool SemaHLSL::transformInitList(const InitializedEntity &Entity,
6345 InitListExpr *Init) {
6346 // If the initializer is a scalar, just return it.
6347 if (Init->getType()->isScalarType())
6348 return true;
6349 ASTContext &Ctx = SemaRef.getASTContext();
6350 InitListTransformer ILT(SemaRef, Entity);
6351
6352 for (unsigned I = 0; I < Init->getNumInits(); ++I) {
6353 Expr *E = Init->getInit(Init: I);
6354 if (E->HasSideEffects(Ctx)) {
6355 QualType Ty = E->getType();
6356 if (Ty->isRecordType())
6357 E = new (Ctx) MaterializeTemporaryExpr(Ty, E, E->isLValue());
6358 E = new (Ctx) OpaqueValueExpr(E->getBeginLoc(), Ty, E->getValueKind(),
6359 E->getObjectKind(), E);
6360 Init->setInit(Init: I, expr: E);
6361 }
6362 if (!ILT.buildInitializerList(E))
6363 return false;
6364 }
6365 size_t ExpectedSize = ILT.DestTypes.size();
6366 size_t ActualSize = ILT.ArgExprs.size();
6367 if (ExpectedSize == 0 && ActualSize == 0)
6368 return true;
6369
6370 // Reject empty initializer if *any* incomplete array exists structurally
6371 if (ActualSize == 0 && containsIncompleteArrayType(Ty: Entity.getType())) {
6372 QualType InitTy = Entity.getType().getNonReferenceType();
6373 if (InitTy.hasAddressSpace())
6374 InitTy = SemaRef.getASTContext().removeAddrSpaceQualType(T: InitTy);
6375
6376 SemaRef.Diag(Loc: Init->getBeginLoc(), DiagID: diag::err_hlsl_incorrect_num_initializers)
6377 << /*TooManyOrFew=*/(int)(ExpectedSize < ActualSize) << InitTy
6378 << /*ExpectedSize=*/ExpectedSize << /*ActualSize=*/ActualSize;
6379 return false;
6380 }
6381
6382 // We infer size after validating legality.
6383 // For incomplete arrays it is completely arbitrary to choose whether we think
6384 // the user intended fewer or more elements. This implementation assumes that
6385 // the user intended more, and errors that there are too few initializers to
6386 // complete the final element.
6387 if (Entity.getType()->isIncompleteArrayType()) {
6388 assert(ExpectedSize > 0 &&
6389 "The expected size of an incomplete array type must be at least 1.");
6390 ExpectedSize =
6391 ((ActualSize + ExpectedSize - 1) / ExpectedSize) * ExpectedSize;
6392 }
6393
6394 // An initializer list might be attempting to initialize a reference or
6395 // rvalue-reference. When checking the initializer we should look through
6396 // the reference.
6397 QualType InitTy = Entity.getType().getNonReferenceType();
6398 if (InitTy.hasAddressSpace())
6399 InitTy = SemaRef.getASTContext().removeAddrSpaceQualType(T: InitTy);
6400 if (ExpectedSize != ActualSize) {
6401 int TooManyOrFew = ActualSize > ExpectedSize ? 1 : 0;
6402 SemaRef.Diag(Loc: Init->getBeginLoc(), DiagID: diag::err_hlsl_incorrect_num_initializers)
6403 << TooManyOrFew << InitTy << ExpectedSize << ActualSize;
6404 return false;
6405 }
6406
6407 // generateInitListsImpl will always return an InitListExpr here, because the
6408 // scalar case is handled above.
6409 auto *NewInit = cast<InitListExpr>(Val: ILT.generateInitLists());
6410 Init->resizeInits(Context: Ctx, NumInits: NewInit->getNumInits());
6411 for (unsigned I = 0; I < NewInit->getNumInits(); ++I)
6412 Init->updateInit(C: Ctx, Init: I, expr: NewInit->getInit(Init: I));
6413 return true;
6414}
6415
6416static QualType ReportMatrixInvalidMember(Sema &S, StringRef Name,
6417 StringRef Expected,
6418 SourceLocation OpLoc,
6419 SourceLocation CompLoc) {
6420 S.Diag(Loc: OpLoc, DiagID: diag::err_builtin_matrix_invalid_member)
6421 << Name << Expected << SourceRange(CompLoc);
6422 return QualType();
6423}
6424
6425QualType SemaHLSL::checkMatrixComponent(Sema &S, QualType baseType,
6426 ExprValueKind &VK, SourceLocation OpLoc,
6427 const IdentifierInfo *CompName,
6428 SourceLocation CompLoc) {
6429 const auto *MT = baseType->castAs<ConstantMatrixType>();
6430 StringRef AccessorName = CompName->getName();
6431 assert(!AccessorName.empty() && "Matrix Accessor must have a name");
6432
6433 unsigned Rows = MT->getNumRows();
6434 unsigned Cols = MT->getNumColumns();
6435 bool IsZeroBasedAccessor = false;
6436 unsigned ChunkLen = 0;
6437 if (AccessorName.size() < 2)
6438 return ReportMatrixInvalidMember(S, Name: AccessorName,
6439 Expected: "length 4 for zero based: \'_mRC\' or "
6440 "length 3 for one-based: \'_RC\' accessor",
6441 OpLoc, CompLoc);
6442
6443 if (AccessorName[0] == '_') {
6444 if (AccessorName[1] == 'm') {
6445 IsZeroBasedAccessor = true;
6446 ChunkLen = 4; // zero-based: "_mRC"
6447 } else {
6448 ChunkLen = 3; // one-based: "_RC"
6449 }
6450 } else
6451 return ReportMatrixInvalidMember(
6452 S, Name: AccessorName, Expected: "zero based: \'_mRC\' or one-based: \'_RC\' accessor",
6453 OpLoc, CompLoc);
6454
6455 if (AccessorName.size() % ChunkLen != 0) {
6456 const llvm::StringRef Expected = IsZeroBasedAccessor
6457 ? "zero based: '_mRC' accessor"
6458 : "one-based: '_RC' accessor";
6459
6460 return ReportMatrixInvalidMember(S, Name: AccessorName, Expected, OpLoc, CompLoc);
6461 }
6462
6463 auto isDigit = [](char c) { return c >= '0' && c <= '9'; };
6464 auto isZeroBasedIndex = [](unsigned i) { return i <= 3; };
6465 auto isOneBasedIndex = [](unsigned i) { return i >= 1 && i <= 4; };
6466
6467 bool HasRepeated = false;
6468 SmallVector<bool, 16> Seen(Rows * Cols, false);
6469 unsigned NumComponents = 0;
6470 const char *Begin = AccessorName.data();
6471
6472 for (unsigned I = 0, E = AccessorName.size(); I < E; I += ChunkLen) {
6473 const char *Chunk = Begin + I;
6474 char RowChar = 0, ColChar = 0;
6475 if (IsZeroBasedAccessor) {
6476 // Zero-based: "_mRC"
6477 if (Chunk[0] != '_' || Chunk[1] != 'm') {
6478 char Bad = (Chunk[0] != '_') ? Chunk[0] : Chunk[1];
6479 return ReportMatrixInvalidMember(
6480 S, Name: StringRef(&Bad, 1), Expected: "\'_m\' prefix",
6481 OpLoc: OpLoc.getLocWithOffset(Offset: I + (Bad == Chunk[0] ? 1 : 2)), CompLoc);
6482 }
6483 RowChar = Chunk[2];
6484 ColChar = Chunk[3];
6485 } else {
6486 // One-based: "_RC"
6487 if (Chunk[0] != '_')
6488 return ReportMatrixInvalidMember(
6489 S, Name: StringRef(&Chunk[0], 1), Expected: "\'_\' prefix",
6490 OpLoc: OpLoc.getLocWithOffset(Offset: I + 1), CompLoc);
6491 RowChar = Chunk[1];
6492 ColChar = Chunk[2];
6493 }
6494
6495 // Must be digits.
6496 bool IsDigitsError = false;
6497 if (!isDigit(RowChar)) {
6498 unsigned BadPos = IsZeroBasedAccessor ? 2 : 1;
6499 ReportMatrixInvalidMember(S, Name: StringRef(&RowChar, 1), Expected: "row as integer",
6500 OpLoc: OpLoc.getLocWithOffset(Offset: I + BadPos + 1),
6501 CompLoc);
6502 IsDigitsError = true;
6503 }
6504
6505 if (!isDigit(ColChar)) {
6506 unsigned BadPos = IsZeroBasedAccessor ? 3 : 2;
6507 ReportMatrixInvalidMember(S, Name: StringRef(&ColChar, 1), Expected: "column as integer",
6508 OpLoc: OpLoc.getLocWithOffset(Offset: I + BadPos + 1),
6509 CompLoc);
6510 IsDigitsError = true;
6511 }
6512 if (IsDigitsError)
6513 return QualType();
6514
6515 unsigned Row = RowChar - '0';
6516 unsigned Col = ColChar - '0';
6517
6518 bool HasIndexingError = false;
6519 if (IsZeroBasedAccessor) {
6520 // 0-based [0..3]
6521 if (!isZeroBasedIndex(Row)) {
6522 S.Diag(Loc: OpLoc, DiagID: diag::err_hlsl_matrix_element_not_in_bounds)
6523 << /*row*/ 0 << /*zero-based*/ 0 << SourceRange(CompLoc);
6524 HasIndexingError = true;
6525 }
6526 if (!isZeroBasedIndex(Col)) {
6527 S.Diag(Loc: OpLoc, DiagID: diag::err_hlsl_matrix_element_not_in_bounds)
6528 << /*col*/ 1 << /*zero-based*/ 0 << SourceRange(CompLoc);
6529 HasIndexingError = true;
6530 }
6531 } else {
6532 // 1-based [1..4]
6533 if (!isOneBasedIndex(Row)) {
6534 S.Diag(Loc: OpLoc, DiagID: diag::err_hlsl_matrix_element_not_in_bounds)
6535 << /*row*/ 0 << /*one-based*/ 1 << SourceRange(CompLoc);
6536 HasIndexingError = true;
6537 }
6538 if (!isOneBasedIndex(Col)) {
6539 S.Diag(Loc: OpLoc, DiagID: diag::err_hlsl_matrix_element_not_in_bounds)
6540 << /*col*/ 1 << /*one-based*/ 1 << SourceRange(CompLoc);
6541 HasIndexingError = true;
6542 }
6543 // Convert to 0-based after range checking.
6544 --Row;
6545 --Col;
6546 }
6547
6548 if (HasIndexingError)
6549 return QualType();
6550
6551 // Note: matrix swizzle index is hard coded. That means Row and Col can
6552 // potentially be larger than Rows and Cols if matrix size is less than
6553 // the max index size.
6554 bool HasBoundsError = false;
6555 if (Row >= Rows) {
6556 Diag(Loc: OpLoc, DiagID: diag::err_hlsl_matrix_index_out_of_bounds)
6557 << /*Row*/ 0 << Row << Rows << SourceRange(CompLoc);
6558 HasBoundsError = true;
6559 }
6560 if (Col >= Cols) {
6561 Diag(Loc: OpLoc, DiagID: diag::err_hlsl_matrix_index_out_of_bounds)
6562 << /*Col*/ 1 << Col << Cols << SourceRange(CompLoc);
6563 HasBoundsError = true;
6564 }
6565 if (HasBoundsError)
6566 return QualType();
6567
6568 unsigned FlatIndex = Row * Cols + Col;
6569 if (Seen[FlatIndex])
6570 HasRepeated = true;
6571 Seen[FlatIndex] = true;
6572 ++NumComponents;
6573 }
6574 if (NumComponents == 0 || NumComponents > 4) {
6575 S.Diag(Loc: OpLoc, DiagID: diag::err_hlsl_matrix_swizzle_invalid_length)
6576 << NumComponents << SourceRange(CompLoc);
6577 return QualType();
6578 }
6579
6580 QualType ElemTy = MT->getElementType();
6581 if (NumComponents == 1)
6582 return ElemTy;
6583 QualType VT = S.Context.getExtVectorType(VectorType: ElemTy, NumElts: NumComponents);
6584 if (HasRepeated)
6585 VK = VK_PRValue;
6586
6587 for (Sema::ExtVectorDeclsType::iterator
6588 I = S.ExtVectorDecls.begin(source: S.getExternalSource()),
6589 E = S.ExtVectorDecls.end();
6590 I != E; ++I) {
6591 if ((*I)->getUnderlyingType() == VT)
6592 return S.Context.getTypedefType(Keyword: ElaboratedTypeKeyword::None,
6593 /*Qualifier=*/std::nullopt, Decl: *I);
6594 }
6595
6596 return VT;
6597}
6598
6599bool SemaHLSL::handleInitialization(VarDecl *VDecl, Expr *&Init) {
6600 // If initializing a local resource, track the resource binding it is using
6601 if (VDecl->getType()->isHLSLResourceRecord() && !VDecl->hasGlobalStorage())
6602 trackLocalResource(VD: VDecl, E: Init);
6603
6604 const HLSLVkConstantIdAttr *ConstIdAttr =
6605 VDecl->getAttr<HLSLVkConstantIdAttr>();
6606 if (!ConstIdAttr)
6607 return true;
6608
6609 ASTContext &Context = SemaRef.getASTContext();
6610
6611 APValue InitValue;
6612 if (!Init->isCXX11ConstantExpr(Ctx: Context, Result: &InitValue)) {
6613 Diag(Loc: VDecl->getLocation(), DiagID: diag::err_specialization_const);
6614 VDecl->setInvalidDecl();
6615 return false;
6616 }
6617
6618 Builtin::ID BID =
6619 getSpecConstBuiltinId(Type: VDecl->getType()->getUnqualifiedDesugaredType());
6620
6621 // Argument 1: The ID from the attribute
6622 int ConstantID = ConstIdAttr->getId();
6623 llvm::APInt IDVal(Context.getIntWidth(T: Context.IntTy), ConstantID);
6624 Expr *IdExpr = IntegerLiteral::Create(C: Context, V: IDVal, type: Context.IntTy,
6625 l: ConstIdAttr->getLocation());
6626
6627 SmallVector<Expr *, 2> Args = {IdExpr, Init};
6628 Expr *C = SemaRef.BuildBuiltinCallExpr(Loc: Init->getExprLoc(), Id: BID, CallArgs: Args);
6629 if (C->getType()->getCanonicalTypeUnqualified() !=
6630 VDecl->getType()->getCanonicalTypeUnqualified()) {
6631 C = SemaRef
6632 .BuildCStyleCastExpr(LParenLoc: SourceLocation(),
6633 Ty: Context.getTrivialTypeSourceInfo(
6634 T: Init->getType(), Loc: Init->getExprLoc()),
6635 RParenLoc: SourceLocation(), Op: C)
6636 .get();
6637 }
6638 Init = C;
6639 return true;
6640}
6641
6642QualType SemaHLSL::ActOnTemplateShorthand(TemplateDecl *Template,
6643 SourceLocation NameLoc) {
6644 if (!Template)
6645 return QualType();
6646
6647 DeclContext *DC = Template->getDeclContext();
6648 if (!DC->isNamespace() || !cast<NamespaceDecl>(Val: DC)->getIdentifier() ||
6649 cast<NamespaceDecl>(Val: DC)->getName() != "hlsl")
6650 return QualType();
6651
6652 TemplateParameterList *Params = Template->getTemplateParameters();
6653 if (!Params || Params->size() != 1)
6654 return QualType();
6655
6656 if (!Template->isImplicit())
6657 return QualType();
6658
6659 // We manually extract default arguments here instead of letting
6660 // CheckTemplateIdType handle it. This ensures that for resource types that
6661 // lack a default argument (like Buffer), we return a null QualType, which
6662 // triggers the "requires template arguments" error rather than a less
6663 // descriptive "too few template arguments" error.
6664 TemplateArgumentListInfo TemplateArgs(NameLoc, NameLoc);
6665 for (NamedDecl *P : *Params) {
6666 if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(Val: P)) {
6667 if (TTP->hasDefaultArgument()) {
6668 TemplateArgs.addArgument(Loc: TTP->getDefaultArgument());
6669 continue;
6670 }
6671 } else if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Val: P)) {
6672 if (NTTP->hasDefaultArgument()) {
6673 TemplateArgs.addArgument(Loc: NTTP->getDefaultArgument());
6674 continue;
6675 }
6676 } else if (auto *TTPD = dyn_cast<TemplateTemplateParmDecl>(Val: P)) {
6677 if (TTPD->hasDefaultArgument()) {
6678 TemplateArgs.addArgument(Loc: TTPD->getDefaultArgument());
6679 continue;
6680 }
6681 }
6682 return QualType();
6683 }
6684
6685 return SemaRef.CheckTemplateIdType(
6686 Keyword: ElaboratedTypeKeyword::None, Template: TemplateName(Template), TemplateLoc: NameLoc,
6687 TemplateArgs, Scope: nullptr, /*ForNestedNameSpecifier=*/false);
6688}
6689