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