1//===--- HLSLBuiltinTypeDeclBuilder.cpp - HLSL Builtin Type Decl Builder --===//
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//
9// Helper classes for creating HLSL builtin class types. Used by external HLSL
10// sema source.
11//
12//===----------------------------------------------------------------------===//
13
14#include "HLSLBuiltinTypeDeclBuilder.h"
15#include "clang/AST/ASTContext.h"
16#include "clang/AST/Attr.h"
17#include "clang/AST/Decl.h"
18#include "clang/AST/DeclCXX.h"
19#include "clang/AST/DeclFriend.h"
20#include "clang/AST/DeclTemplate.h"
21#include "clang/AST/Expr.h"
22#include "clang/AST/HLSLResource.h"
23#include "clang/AST/Stmt.h"
24#include "clang/AST/Type.h"
25#include "clang/Basic/SourceLocation.h"
26#include "clang/Basic/Specifiers.h"
27#include "clang/Sema/Lookup.h"
28#include "clang/Sema/Sema.h"
29#include "clang/Sema/SemaHLSL.h"
30#include "llvm/ADT/SmallVector.h"
31
32using namespace llvm::hlsl;
33
34namespace clang {
35
36namespace hlsl {
37
38namespace {
39
40static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name) {
41 IdentifierInfo &II =
42 S.getASTContext().Idents.get(Name, TokenCode: tok::TokenKind::identifier);
43 DeclarationNameInfo NameInfo =
44 DeclarationNameInfo(DeclarationName(&II), SourceLocation());
45 LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
46 // AllowBuiltinCreation is false but LookupDirect will create
47 // the builtin when searching the global scope anyways...
48 S.LookupName(R, S: S.getCurScope());
49 // FIXME: If the builtin function was user-declared in global scope,
50 // this assert *will* fail. Should this call LookupBuiltin instead?
51 assert(R.isSingleResult() &&
52 "Since this is a builtin it should always resolve!");
53 return cast<FunctionDecl>(Val: R.getFoundDecl());
54}
55
56static NamespaceDecl *lookupBuiltinNamespace(Sema &S, StringRef Name,
57 DeclContext *DC) {
58 IdentifierInfo &II =
59 S.getASTContext().Idents.get(Name, TokenCode: tok::TokenKind::identifier);
60 LookupResult Result(S, &II, SourceLocation(), Sema::LookupNamespaceName);
61 S.LookupQualifiedName(R&: Result, LookupCtx: DC);
62 assert(!Result.empty() && "Builtin namespace not found");
63 return Result.getAsSingle<NamespaceDecl>();
64}
65
66static QualType lookupBuiltinType(Sema &S, StringRef Name, DeclContext *DC) {
67 IdentifierInfo &II =
68 S.getASTContext().Idents.get(Name, TokenCode: tok::TokenKind::identifier);
69 LookupResult Result(S, &II, SourceLocation(), Sema::LookupTagName);
70 S.LookupQualifiedName(R&: Result, LookupCtx: DC);
71 assert(!Result.empty() && "Builtin type not found");
72 QualType Ty =
73 S.getASTContext().getTypeDeclType(Decl: Result.getAsSingle<TypeDecl>());
74 S.RequireCompleteType(Loc: SourceLocation(), T: Ty,
75 DiagID: diag::err_tentative_def_incomplete_type);
76 return Ty;
77}
78
79CXXConstructorDecl *lookupCopyConstructor(QualType ResTy) {
80 assert(ResTy->isRecordType() && "not a CXXRecord type");
81 for (auto *CD : ResTy->getAsCXXRecordDecl()->ctors())
82 if (CD->isCopyConstructor())
83 return CD;
84 return nullptr;
85}
86
87ParameterABI
88convertParamModifierToParamABI(HLSLParamModifierAttr::Spelling Modifier) {
89 assert(Modifier != HLSLParamModifierAttr::Spelling::Keyword_in &&
90 "HLSL 'in' parameters modifier cannot be converted to ParameterABI");
91 switch (Modifier) {
92 case HLSLParamModifierAttr::Spelling::Keyword_out:
93 return ParameterABI::HLSLOut;
94 case HLSLParamModifierAttr::Spelling::Keyword_inout:
95 return ParameterABI::HLSLInOut;
96 default:
97 llvm_unreachable("Invalid HLSL parameter modifier");
98 }
99}
100
101QualType getVectorOrScalarType(ASTContext &AST, QualType Ty,
102 uint32_t NumElements) {
103 assert(NumElements > 0 && "Cannot create a zero-element type");
104 return NumElements > 1 ? AST.getExtVectorType(VectorType: Ty, NumElts: NumElements) : Ty;
105}
106
107QualType getInoutParameterType(ASTContext &AST, QualType Ty) {
108 assert(!Ty->isReferenceType() &&
109 "Pointer and reference types cannot be inout or out parameters");
110 Ty = AST.getLValueReferenceType(T: Ty);
111 Ty.addRestrict();
112 return Ty;
113}
114
115// Attaches availability attributes to a method that requires implicit
116// derivatives. Implicit derivatives are always available in pixel
117// shaders. Shader Model 6.6 made derivatives available in compute, mesh and
118// amplification shaders as well. All other shader stages do not support
119// derivatives.
120void addDerivativeAvailabilityAttrs(ASTContext &AST, FunctionDecl *FD) {
121 struct DerivativeShaderStage {
122 StringRef Environment;
123 VersionTuple Introduced;
124 };
125 const DerivativeShaderStage Stages[] = {
126 {.Environment: "pixel", .Introduced: VersionTuple(6, 0)},
127 {.Environment: "compute", .Introduced: VersionTuple(6, 6)},
128 {.Environment: "mesh", .Introduced: VersionTuple(6, 6)},
129 {.Environment: "amplification", .Introduced: VersionTuple(6, 6)},
130 };
131
132 const IdentifierInfo *Platform = &AST.Idents.get(Name: "shadermodel");
133 for (const DerivativeShaderStage &Stage : Stages)
134 FD->addAttr(A: AvailabilityAttr::CreateImplicit(
135 Ctx&: AST, Platform, Introduced: Stage.Introduced, /*Deprecated=*/VersionTuple(),
136 /*Obsoleted=*/VersionTuple(), /*Unavailable=*/false, /*Message=*/"",
137 /*Strict=*/false, /*Replacement=*/"", Priority: Sema::AP_Explicit,
138 Environment: &AST.Idents.get(Name: Stage.Environment), /*InferredAttr=*/nullptr));
139}
140
141} // namespace
142
143// Builder for template arguments of builtin types. Used internally
144// by BuiltinTypeDeclBuilder.
145struct TemplateParameterListBuilder {
146 BuiltinTypeDeclBuilder &Builder;
147 llvm::SmallVector<NamedDecl *> Params;
148
149 TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB) : Builder(RB) {}
150 ~TemplateParameterListBuilder();
151
152 TemplateParameterListBuilder &
153 addTypeParameter(StringRef Name, QualType DefaultValue = QualType());
154
155 TemplateParameterListBuilder &
156 addNonTypeParameter(StringRef Name, QualType Ty,
157 Expr *DefaultValue = nullptr);
158
159 ConceptSpecializationExpr *
160 constructConceptSpecializationExpr(Sema &S, ConceptDecl *CD);
161
162 BuiltinTypeDeclBuilder &finalizeTemplateArgs(ConceptDecl *CD = nullptr);
163};
164
165// Builder for methods or constructors of builtin types. Allows creating methods
166// or constructors of builtin types using the builder pattern like this:
167//
168// BuiltinTypeMethodBuilder(RecordBuilder, "MethodName", ReturnType)
169// .addParam("param_name", Type, InOutModifier)
170// .callBuiltin("builtin_name", BuiltinParams...)
171// .finalize();
172//
173// The builder needs to have all of the parameters before it can create
174// a CXXMethodDecl or CXXConstructorDecl. It collects them in addParam calls and
175// when a first method that builds the body is called or when access to 'this`
176// is needed it creates the CXXMethodDecl/CXXConstructorDecl and ParmVarDecls
177// instances. These can then be referenced from the body building methods.
178// Destructor or an explicit call to finalize() will complete the method
179// definition.
180//
181// The callBuiltin helper method accepts constants via `Expr *` or placeholder
182// value arguments to indicate which function arguments to forward to the
183// builtin.
184//
185// If the method that is being built has a non-void return type the
186// finalize() will create a return statement with the value of the last
187// statement (unless the last statement is already a ReturnStmt or the return
188// value is void).
189struct BuiltinTypeMethodBuilder {
190private:
191 struct Param {
192 const IdentifierInfo &NameII;
193 QualType Ty;
194 HLSLParamModifierAttr::Spelling Modifier;
195 Param(const IdentifierInfo &NameII, QualType Ty,
196 HLSLParamModifierAttr::Spelling Modifier)
197 : NameII(NameII), Ty(Ty), Modifier(Modifier) {}
198 };
199
200 struct LocalVar {
201 StringRef Name;
202 QualType Ty;
203 VarDecl *Decl;
204 LocalVar(StringRef Name, QualType Ty) : Name(Name), Ty(Ty), Decl(nullptr) {}
205 };
206
207 BuiltinTypeDeclBuilder &DeclBuilder;
208 DeclarationName Name;
209 QualType ReturnTy;
210 // method or constructor declaration
211 // (CXXConstructorDecl derives from CXXMethodDecl)
212 CXXMethodDecl *Method;
213 bool IsConst;
214 bool IsCtor;
215 StorageClass SC;
216 llvm::SmallVector<Param> Params;
217 llvm::SmallVector<Stmt *> StmtsList;
218 TemplateParameterList *TemplateParams = nullptr;
219 llvm::SmallVector<NamedDecl *> TemplateParamDecls;
220
221 // Argument placeholders, inspired by std::placeholder. These are the indices
222 // of arguments to forward to `callBuiltin` and other method builder methods.
223 // Additional special values are:
224 // Handle - refers to the resource handle.
225 // LastStmt - refers to the last statement in the method body; referencing
226 // LastStmt will remove the statement from the method body since
227 // it will be linked from the new expression being constructed.
228 enum class PlaceHolder {
229 _0,
230 _1,
231 _2,
232 _3,
233 _4,
234 _5,
235 Handle = 128,
236 CounterHandle,
237 This,
238 LastStmt
239 };
240
241 Expr *convertPlaceholder(PlaceHolder PH);
242 Expr *convertPlaceholder(LocalVar &Var);
243 Expr *convertPlaceholder(Expr *E) { return E; }
244 // Converts a QualType to an Expr that carries type information to builtins.
245 Expr *convertPlaceholder(QualType Ty);
246
247public:
248 friend BuiltinTypeDeclBuilder;
249
250 BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, DeclarationName &Name,
251 QualType ReturnTy, bool IsConst = false,
252 bool IsCtor = false, StorageClass SC = SC_None)
253 : DeclBuilder(DB), Name(Name), ReturnTy(ReturnTy), Method(nullptr),
254 IsConst(IsConst), IsCtor(IsCtor), SC(SC) {}
255
256 BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, StringRef NameStr,
257 QualType ReturnTy, bool IsConst = false,
258 bool IsCtor = false, StorageClass SC = SC_None);
259 BuiltinTypeMethodBuilder(const BuiltinTypeMethodBuilder &Other) = delete;
260
261 ~BuiltinTypeMethodBuilder() { finalize(); }
262
263 BuiltinTypeMethodBuilder &
264 operator=(const BuiltinTypeMethodBuilder &Other) = delete;
265
266 BuiltinTypeMethodBuilder &addParam(StringRef Name, QualType Ty,
267 HLSLParamModifierAttr::Spelling Modifier =
268 HLSLParamModifierAttr::Keyword_in);
269 QualType addTemplateTypeParam(StringRef Name);
270 BuiltinTypeMethodBuilder &declareLocalVar(LocalVar &Var);
271 template <typename... Ts>
272 BuiltinTypeMethodBuilder &callBuiltin(StringRef BuiltinName,
273 QualType ReturnType, Ts &&...ArgSpecs);
274 template <typename TLHS, typename TRHS>
275 BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS);
276 template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr);
277 template <typename V, typename S>
278 BuiltinTypeMethodBuilder &concat(V Vec, S Scalar, QualType ResultTy);
279
280 template <typename T>
281 BuiltinTypeMethodBuilder &accessHandleFieldOnResource(T ResourceRecord);
282 template <typename T>
283 BuiltinTypeMethodBuilder &accessFieldOnResource(T ResourceRecord,
284 FieldDecl *Field);
285 template <typename ValueT>
286 BuiltinTypeMethodBuilder &setHandleFieldOnResource(LocalVar &ResourceRecord,
287 ValueT HandleValue);
288 template <typename ResourceT, typename ValueT>
289 BuiltinTypeMethodBuilder &setFieldOnResource(ResourceT ResourceRecord,
290 ValueT HandleValue,
291 FieldDecl *HandleField);
292 void setMipsHandleField(LocalVar &ResourceRecord);
293 template <typename T>
294 BuiltinTypeMethodBuilder &
295 accessCounterHandleFieldOnResource(T ResourceRecord);
296 template <typename ResourceT, typename ValueT>
297 BuiltinTypeMethodBuilder &
298 setCounterHandleFieldOnResource(ResourceT ResourceRecord, ValueT HandleValue);
299 template <typename T> BuiltinTypeMethodBuilder &returnValue(T ReturnValue);
300 BuiltinTypeMethodBuilder &returnThis();
301 BuiltinTypeDeclBuilder &
302 finalize(AccessSpecifier Access = AccessSpecifier::AS_public);
303 Expr *getResourceHandleExpr();
304 Expr *getResourceCounterHandleExpr();
305
306 template <typename T> MemberExpr *createMemberExpr(T Base, FieldDecl *Field);
307 CXXThisExpr *createThisExpr();
308
309private:
310 void createDecl();
311
312 // Makes sure the declaration is created; should be called before any
313 // statement added to the body or when access to 'this' is needed.
314 void ensureCompleteDecl() {
315 if (!Method)
316 createDecl();
317 }
318
319 ASTContext &getASTContext() { return DeclBuilder.SemaRef.getASTContext(); }
320};
321
322TemplateParameterListBuilder::~TemplateParameterListBuilder() {
323 finalizeTemplateArgs();
324}
325
326TemplateParameterListBuilder &
327TemplateParameterListBuilder::addTypeParameter(StringRef Name,
328 QualType DefaultValue) {
329 assert(!Builder.Record->isCompleteDefinition() &&
330 "record is already complete");
331 ASTContext &AST = Builder.SemaRef.getASTContext();
332 unsigned Position = static_cast<unsigned>(Params.size());
333 auto *Decl = TemplateTypeParmDecl::Create(
334 C: AST, DC: Builder.Record->getDeclContext(), KeyLoc: SourceLocation(), NameLoc: SourceLocation(),
335 /* TemplateDepth */ D: 0, P: Position,
336 Id: &AST.Idents.get(Name, TokenCode: tok::TokenKind::identifier),
337 /* Typename */ true,
338 /* ParameterPack */ false,
339 /* HasTypeConstraint*/ false);
340 if (!DefaultValue.isNull())
341 Decl->setDefaultArgument(C: AST,
342 DefArg: Builder.SemaRef.getTrivialTemplateArgumentLoc(
343 Arg: DefaultValue, NTTPType: QualType(), Loc: SourceLocation()));
344
345 Params.emplace_back(Args&: Decl);
346 return *this;
347}
348
349TemplateParameterListBuilder &
350TemplateParameterListBuilder::addNonTypeParameter(StringRef Name, QualType Ty,
351 Expr *DefaultValue) {
352 assert(!Builder.Record->isCompleteDefinition() &&
353 "record is already complete");
354 ASTContext &AST = Builder.SemaRef.getASTContext();
355 unsigned Position = static_cast<unsigned>(Params.size());
356 auto *Decl = NonTypeTemplateParmDecl::Create(
357 C: AST, DC: Builder.Record->getDeclContext(), StartLoc: SourceLocation(), IdLoc: SourceLocation(),
358 /* TemplateDepth */ D: 0, P: Position,
359 Id: &AST.Idents.get(Name, TokenCode: tok::TokenKind::identifier), T: Ty,
360 /* ParameterPack */ false, TInfo: AST.getTrivialTypeSourceInfo(T: Ty));
361 if (DefaultValue)
362 Decl->setDefaultArgument(
363 C: AST, DefArg: Builder.SemaRef.getTrivialTemplateArgumentLoc(
364 Arg: TemplateArgument(DefaultValue, /*IsCanonical=*/false), NTTPType: Ty,
365 Loc: SourceLocation()));
366
367 Params.emplace_back(Args&: Decl);
368 return *this;
369}
370
371// The concept specialization expression (CSE) constructed in
372// constructConceptSpecializationExpr is constructed so that it
373// matches the CSE that is constructed when parsing the below C++ code:
374//
375// template<typename T>
376// concept is_typed_resource_element_compatible =
377// __builtin_hlsl_typed_resource_element_compatible<T>
378//
379// template<typename element_type> requires
380// is_typed_resource_element_compatible<element_type>
381// struct RWBuffer {
382// element_type Val;
383// };
384//
385// int fn() {
386// RWBuffer<int> Buf;
387// }
388//
389// When dumping the AST and filtering for "RWBuffer", the resulting AST
390// structure is what we're trying to construct below, specifically the
391// CSE portion.
392ConceptSpecializationExpr *
393TemplateParameterListBuilder::constructConceptSpecializationExpr(
394 Sema &S, ConceptDecl *CD) {
395 ASTContext &Context = S.getASTContext();
396 SourceLocation Loc = Builder.Record->getBeginLoc();
397 DeclarationNameInfo DNI(CD->getDeclName(), Loc);
398 NestedNameSpecifierLoc NNSLoc;
399 DeclContext *DC = Builder.Record->getDeclContext();
400 TemplateArgumentListInfo TALI(Loc, Loc);
401
402 // Assume that the concept decl has just one template parameter
403 // This parameter should have been added when CD was constructed
404 // in getTypedBufferConceptDecl
405 assert(CD->getTemplateParameters()->size() == 1 &&
406 "unexpected concept decl parameter count");
407 TemplateTypeParmDecl *ConceptTTPD =
408 dyn_cast<TemplateTypeParmDecl>(Val: CD->getTemplateParameters()->getParam(Idx: 0));
409
410 // this TemplateTypeParmDecl is the template for the resource, and is
411 // used to construct a template argumentthat will be used
412 // to construct the ImplicitConceptSpecializationDecl
413 TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create(
414 C: Context, // AST context
415 DC: Builder.Record->getDeclContext(), // DeclContext
416 KeyLoc: SourceLocation(), NameLoc: SourceLocation(),
417 /*D=*/0, // Depth in the template parameter list
418 /*P=*/0, // Position in the template parameter list
419 /*Id=*/nullptr, // Identifier for 'T'
420 /*Typename=*/true, // Indicates this is a 'typename' or 'class'
421 /*ParameterPack=*/false, // Not a parameter pack
422 /*HasTypeConstraint=*/false // Has no type constraint
423 );
424
425 T->setDeclContext(DC);
426
427 QualType ConceptTType = Context.getTypeDeclType(Decl: ConceptTTPD);
428
429 // this is the 2nd template argument node, on which
430 // the concept constraint is actually being applied: 'element_type'
431 TemplateArgument ConceptTA = TemplateArgument(ConceptTType);
432
433 QualType CSETType = Context.getTypeDeclType(Decl: T);
434
435 // this is the 1st template argument node, which represents
436 // the abstract type that a concept would refer to: 'T'
437 TemplateArgument CSETA = TemplateArgument(CSETType);
438
439 ImplicitConceptSpecializationDecl *ImplicitCSEDecl =
440 ImplicitConceptSpecializationDecl::Create(
441 C: Context, DC: Builder.Record->getDeclContext(), SL: Loc, ConvertedArgs: {CSETA});
442
443 // Constraint satisfaction is used to construct the
444 // ConceptSpecailizationExpr, and represents the 2nd Template Argument,
445 // located at the bottom of the sample AST above.
446 const ConstraintSatisfaction CS(CD, {ConceptTA});
447 TemplateArgumentLoc TAL =
448 S.getTrivialTemplateArgumentLoc(Arg: ConceptTA, NTTPType: QualType(), Loc: SourceLocation());
449
450 TALI.addArgument(Loc: TAL);
451 const ASTTemplateArgumentListInfo *ATALI =
452 ASTTemplateArgumentListInfo::Create(C: Context, List: TALI);
453
454 // In the concept reference, ATALI is what adds the extra
455 // TemplateArgument node underneath CSE
456 ConceptReference *CR = ConceptReference::Create(C: Context, NNS: NNSLoc, TemplateKWLoc: Loc, ConceptNameInfo: DNI, FoundDecl: CD,
457 NamedConcept: TemplateName(CD), ArgsAsWritten: ATALI);
458
459 ConceptSpecializationExpr *CSE =
460 ConceptSpecializationExpr::Create(C: Context, ConceptRef: CR, SpecDecl: ImplicitCSEDecl, Satisfaction: &CS);
461
462 return CSE;
463}
464
465BuiltinTypeDeclBuilder &
466TemplateParameterListBuilder::finalizeTemplateArgs(ConceptDecl *CD) {
467 if (Params.empty())
468 return Builder;
469
470 ASTContext &AST = Builder.SemaRef.Context;
471 ConceptSpecializationExpr *CSE =
472 CD ? constructConceptSpecializationExpr(S&: Builder.SemaRef, CD) : nullptr;
473 auto *ParamList = TemplateParameterList::Create(
474 C: AST, TemplateLoc: SourceLocation(), LAngleLoc: SourceLocation(), Params, RAngleLoc: SourceLocation(), RequiresClause: CSE);
475 Builder.Template = ClassTemplateDecl::Create(
476 C&: AST, DC: Builder.Record->getDeclContext(), L: SourceLocation(),
477 Name: DeclarationName(Builder.Record->getIdentifier()), Params: ParamList,
478 Decl: Builder.Record);
479
480 Builder.Record->setDescribedClassTemplate(Builder.Template);
481 Builder.Template->setImplicit(true);
482 Builder.Template->setLexicalDeclContext(Builder.Record->getDeclContext());
483
484 // NOTE: setPreviousDecl before addDecl so new decl replace old decl when
485 // make visible.
486 Builder.Template->setPreviousDecl(Builder.PrevTemplate);
487 Builder.Record->getDeclContext()->addDecl(D: Builder.Template);
488 Params.clear();
489
490 return Builder;
491}
492
493Expr *BuiltinTypeMethodBuilder::convertPlaceholder(PlaceHolder PH) {
494 if (PH == PlaceHolder::Handle)
495 return getResourceHandleExpr();
496 if (PH == PlaceHolder::CounterHandle)
497 return getResourceCounterHandleExpr();
498 if (PH == PlaceHolder::This)
499 return createThisExpr();
500
501 if (PH == PlaceHolder::LastStmt) {
502 assert(!StmtsList.empty() && "no statements in the list");
503 Stmt *LastStmt = StmtsList.pop_back_val();
504 assert(isa<ValueStmt>(LastStmt) && "last statement does not have a value");
505 return cast<ValueStmt>(Val: LastStmt)->getExprStmt();
506 }
507
508 // All other placeholders are parameters (_N), and can be loaded as an
509 // LValue. It needs to be an LValue if the result expression will be used as
510 // the actual parameter for an out parameter. The dimension builtins are an
511 // example where this happens.
512 ParmVarDecl *ParamDecl = Method->getParamDecl(i: static_cast<unsigned>(PH));
513 return DeclRefExpr::Create(
514 Context: getASTContext(), QualifierLoc: NestedNameSpecifierLoc(), TemplateKWLoc: SourceLocation(), D: ParamDecl,
515 RefersToEnclosingVariableOrCapture: false, NameInfo: DeclarationNameInfo(ParamDecl->getDeclName(), SourceLocation()),
516 T: ParamDecl->getType().getNonReferenceType(), VK: VK_LValue);
517}
518
519Expr *BuiltinTypeMethodBuilder::convertPlaceholder(LocalVar &Var) {
520 VarDecl *VD = Var.Decl;
521 assert(VD && "local variable is not declared");
522 return DeclRefExpr::Create(
523 Context: VD->getASTContext(), QualifierLoc: NestedNameSpecifierLoc(), TemplateKWLoc: SourceLocation(), D: VD,
524 RefersToEnclosingVariableOrCapture: false, NameInfo: DeclarationNameInfo(VD->getDeclName(), SourceLocation()),
525 T: VD->getType(), VK: VK_LValue);
526}
527
528Expr *BuiltinTypeMethodBuilder::convertPlaceholder(QualType Ty) {
529 ASTContext &AST = getASTContext();
530 QualType PtrTy = AST.getPointerType(T: Ty);
531 // Creates a value-initialized null pointer of type Ty*.
532 return new (AST) CXXScalarValueInitExpr(
533 PtrTy, AST.getTrivialTypeSourceInfo(T: PtrTy, Loc: SourceLocation()),
534 SourceLocation());
535}
536
537BuiltinTypeMethodBuilder::BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB,
538 StringRef NameStr,
539 QualType ReturnTy,
540 bool IsConst, bool IsCtor,
541 StorageClass SC)
542 : DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst),
543 IsCtor(IsCtor), SC(SC) {
544
545 assert((!NameStr.empty() || IsCtor) && "method needs a name");
546 assert(((IsCtor && !IsConst) || !IsCtor) && "constructor cannot be const");
547
548 ASTContext &AST = getASTContext();
549 if (IsCtor) {
550 Name = AST.DeclarationNames.getCXXConstructorName(
551 Ty: AST.getCanonicalTagType(TD: DB.Record));
552 } else {
553 const IdentifierInfo &II =
554 AST.Idents.get(Name: NameStr, TokenCode: tok::TokenKind::identifier);
555 Name = DeclarationName(&II);
556 }
557}
558
559BuiltinTypeMethodBuilder &
560BuiltinTypeMethodBuilder::addParam(StringRef Name, QualType Ty,
561 HLSLParamModifierAttr::Spelling Modifier) {
562 assert(Method == nullptr && "Cannot add param, method already created");
563 const IdentifierInfo &II =
564 getASTContext().Idents.get(Name, TokenCode: tok::TokenKind::identifier);
565 Params.emplace_back(Args: II, Args&: Ty, Args&: Modifier);
566 return *this;
567}
568QualType BuiltinTypeMethodBuilder::addTemplateTypeParam(StringRef Name) {
569 assert(Method == nullptr &&
570 "Cannot add template param, method already created");
571 ASTContext &AST = getASTContext();
572 unsigned Position = static_cast<unsigned>(TemplateParamDecls.size());
573 auto *Decl = TemplateTypeParmDecl::Create(
574 C: AST, DC: DeclBuilder.Record, KeyLoc: SourceLocation(), NameLoc: SourceLocation(),
575 /* TemplateDepth */ D: 0, P: Position,
576 Id: &AST.Idents.get(Name, TokenCode: tok::TokenKind::identifier),
577 /* Typename */ true,
578 /* ParameterPack */ false,
579 /* HasTypeConstraint*/ false);
580 TemplateParamDecls.push_back(Elt: Decl);
581
582 return QualType(Decl->getTypeForDecl(), 0);
583}
584
585void BuiltinTypeMethodBuilder::createDecl() {
586 assert(Method == nullptr && "Method or constructor is already created");
587
588 // create function prototype
589 ASTContext &AST = getASTContext();
590 SmallVector<QualType> ParamTypes;
591 SmallVector<FunctionType::ExtParameterInfo> ParamExtInfos(Params.size());
592 uint32_t ArgIndex = 0;
593
594 // Create function prototype.
595 bool UseParamExtInfo = false;
596 for (Param &MP : Params) {
597 if (MP.Modifier != HLSLParamModifierAttr::Keyword_in) {
598 UseParamExtInfo = true;
599 FunctionType::ExtParameterInfo &PI = ParamExtInfos[ArgIndex];
600 ParamExtInfos[ArgIndex] =
601 PI.withABI(kind: convertParamModifierToParamABI(Modifier: MP.Modifier));
602 if (!MP.Ty->isDependentType())
603 MP.Ty = getInoutParameterType(AST, Ty: MP.Ty);
604 }
605 ParamTypes.emplace_back(Args&: MP.Ty);
606 ++ArgIndex;
607 }
608
609 FunctionProtoType::ExtProtoInfo ExtInfo;
610 if (UseParamExtInfo)
611 ExtInfo.ExtParameterInfos = ParamExtInfos.data();
612 if (IsConst)
613 ExtInfo.TypeQuals.addConst();
614
615 QualType FuncTy = AST.getFunctionType(ResultTy: ReturnTy, Args: ParamTypes, EPI: ExtInfo);
616
617 // Create method or constructor declaration.
618 auto *TSInfo = AST.getTrivialTypeSourceInfo(T: FuncTy, Loc: SourceLocation());
619 DeclarationNameInfo NameInfo = DeclarationNameInfo(Name, SourceLocation());
620 if (IsCtor)
621 Method = CXXConstructorDecl::Create(
622 C&: AST, RD: DeclBuilder.Record, StartLoc: SourceLocation(), NameInfo, T: FuncTy, TInfo: TSInfo,
623 ES: ExplicitSpecifier(), UsesFPIntrin: false, /*IsInline=*/isInline: true, isImplicitlyDeclared: false,
624 ConstexprKind: ConstexprSpecKind::Unspecified);
625 else if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName)
626 Method = CXXConversionDecl::Create(
627 C&: AST, RD: DeclBuilder.Record, StartLoc: SourceLocation(), NameInfo, T: FuncTy, TInfo: TSInfo,
628 UsesFPIntrin: false, /*isInline=*/true, ES: ExplicitSpecifier(),
629 ConstexprKind: ConstexprSpecKind::Unspecified, EndLocation: SourceLocation());
630 else
631 Method = CXXMethodDecl::Create(
632 C&: AST, RD: DeclBuilder.Record, StartLoc: SourceLocation(), NameInfo, T: FuncTy, TInfo: TSInfo, SC,
633 UsesFPIntrin: false, isInline: true, ConstexprKind: ConstexprSpecKind::Unspecified, EndLocation: SourceLocation());
634
635 // Create params & set them to the method/constructor and function prototype.
636 SmallVector<ParmVarDecl *> ParmDecls;
637 unsigned CurScopeDepth = DeclBuilder.SemaRef.getCurScope()->getDepth();
638 auto FnProtoLoc =
639 Method->getTypeSourceInfo()->getTypeLoc().getAs<FunctionProtoTypeLoc>();
640 for (int I = 0, E = Params.size(); I != E; I++) {
641 Param &MP = Params[I];
642 ParmVarDecl *Parm = ParmVarDecl::Create(
643 C&: AST, DC: Method, StartLoc: SourceLocation(), IdLoc: SourceLocation(), Id: &MP.NameII, T: MP.Ty,
644 TInfo: AST.getTrivialTypeSourceInfo(T: MP.Ty, Loc: SourceLocation()), S: SC_None,
645 DefArg: nullptr);
646 if (MP.Modifier != HLSLParamModifierAttr::Keyword_in) {
647 auto *Mod =
648 HLSLParamModifierAttr::Create(Ctx&: AST, Range: SourceRange(), S: MP.Modifier);
649 Parm->addAttr(A: Mod);
650 }
651 Parm->setScopeInfo(scopeDepth: CurScopeDepth, parameterIndex: I);
652 ParmDecls.push_back(Elt: Parm);
653 FnProtoLoc.setParam(i: I, VD: Parm);
654 }
655 Method->setParams({ParmDecls});
656}
657
658Expr *BuiltinTypeMethodBuilder::getResourceHandleExpr() {
659 ensureCompleteDecl();
660 FieldDecl *HandleField = DeclBuilder.getResourceHandleField();
661 return createMemberExpr(Base: createThisExpr(), Member: HandleField);
662}
663
664Expr *BuiltinTypeMethodBuilder::getResourceCounterHandleExpr() {
665 ensureCompleteDecl();
666 FieldDecl *HandleField = DeclBuilder.getResourceCounterHandleField();
667 return createMemberExpr(Base: createThisExpr(), Member: HandleField);
668}
669
670template <typename T>
671MemberExpr *BuiltinTypeMethodBuilder::createMemberExpr(T Base,
672 FieldDecl *Member) {
673 ensureCompleteDecl();
674 Expr *BaseExpr = convertPlaceholder(Base);
675 return MemberExpr::CreateImplicit(C: getASTContext(), Base: BaseExpr, IsArrow: false, MemberDecl: Member,
676 T: Member->getType(), VK: VK_LValue, OK: OK_Ordinary);
677}
678
679CXXThisExpr *BuiltinTypeMethodBuilder::createThisExpr() {
680 CXXThisExpr *This =
681 CXXThisExpr::Create(Ctx: getASTContext(), L: SourceLocation(),
682 Ty: Method->getFunctionObjectParameterType(), IsImplicit: true);
683 return This;
684}
685
686BuiltinTypeMethodBuilder &
687BuiltinTypeMethodBuilder::declareLocalVar(LocalVar &Var) {
688 ensureCompleteDecl();
689
690 assert(Var.Decl == nullptr && "local variable is already declared");
691
692 ASTContext &AST = getASTContext();
693 Var.Decl = VarDecl::Create(
694 C&: AST, DC: Method, StartLoc: SourceLocation(), IdLoc: SourceLocation(),
695 Id: &AST.Idents.get(Name: Var.Name, TokenCode: tok::TokenKind::identifier), T: Var.Ty,
696 TInfo: AST.getTrivialTypeSourceInfo(T: Var.Ty, Loc: SourceLocation()), S: SC_None);
697 DeclStmt *DS = new (AST) clang::DeclStmt(DeclGroupRef(Var.Decl),
698 SourceLocation(), SourceLocation());
699 StmtsList.push_back(Elt: DS);
700 return *this;
701}
702
703template <typename V, typename S>
704BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::concat(V Vec, S Scalar,
705 QualType ResultTy) {
706 assert(ResultTy->isVectorType() && "The result type must be a vector type.");
707 Expr *VecExpr = convertPlaceholder(Vec);
708 Expr *ScalarExpr = convertPlaceholder(Scalar);
709
710 ASTContext &AST = getASTContext();
711 SmallVector<Expr *, 4> Elts;
712 if (const auto *VecTy = VecExpr->getType()->getAs<VectorType>()) {
713 // Save the vector to a local variable to avoid evaluating the placeholder
714 // multiple times or sharing the AST node.
715 LocalVar VecVar("vec_tmp", VecTy->desugar());
716 declareLocalVar(Var&: VecVar);
717 assign(LHS: VecVar, RHS: VecExpr);
718
719 QualType EltTy = VecTy->getElementType();
720 unsigned NumElts = VecTy->getNumElements();
721
722 for (unsigned I = 0; I < NumElts; ++I) {
723 Elts.push_back(Elt: new (AST) ArraySubscriptExpr(
724 convertPlaceholder(Var&: VecVar), DeclBuilder.getConstantIntExpr(value: I), EltTy,
725 VK_PRValue, OK_Ordinary, SourceLocation()));
726 }
727 } else {
728 Elts.push_back(Elt: VecExpr);
729 }
730 Elts.push_back(Elt: ScalarExpr);
731 assert(ResultTy->castAs<VectorType>()->getNumElements() == Elts.size() &&
732 "The result type must have one element per concatenated value.");
733
734 auto *InitList = new (AST) InitListExpr(
735 AST, SourceLocation(), Elts, SourceLocation(), /*isExplicit=*/false);
736 InitList->setType(ResultTy);
737
738 ExprResult Cast = DeclBuilder.SemaRef.BuildCStyleCastExpr(
739 LParenLoc: SourceLocation(), Ty: AST.getTrivialTypeSourceInfo(T: ResultTy),
740 RParenLoc: SourceLocation(), Op: InitList);
741 assert(!Cast.isInvalid() && "Cast cannot fail!");
742 StmtsList.push_back(Elt: Cast.get());
743
744 return *this;
745}
746
747BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::returnThis() {
748 StmtsList.push_back(Elt: createThisExpr());
749 return *this;
750}
751
752template <typename... Ts>
753BuiltinTypeMethodBuilder &
754BuiltinTypeMethodBuilder::callBuiltin(StringRef BuiltinName,
755 QualType ReturnType, Ts &&...ArgSpecs) {
756 ensureCompleteDecl();
757
758 std::array<Expr *, sizeof...(ArgSpecs)> Args{
759 convertPlaceholder(std::forward<Ts>(ArgSpecs))...};
760
761 ASTContext &AST = getASTContext();
762 FunctionDecl *FD = lookupBuiltinFunction(S&: DeclBuilder.SemaRef, Name: BuiltinName);
763 DeclRefExpr *DRE = DeclRefExpr::Create(
764 Context: AST, QualifierLoc: NestedNameSpecifierLoc(), TemplateKWLoc: SourceLocation(), D: FD, RefersToEnclosingVariableOrCapture: false,
765 NameInfo: FD->getNameInfo(), T: AST.BuiltinFnTy, VK: VK_PRValue);
766
767 ExprResult Call = DeclBuilder.SemaRef.BuildCallExpr(
768 /*Scope=*/S: nullptr, Fn: DRE, LParenLoc: SourceLocation(),
769 ArgExprs: MultiExprArg(Args.data(), Args.size()), RParenLoc: SourceLocation());
770 assert(!Call.isInvalid() && "Call to builtin cannot fail!");
771 Expr *E = Call.get();
772
773 if (!ReturnType.isNull() &&
774 !AST.hasSameUnqualifiedType(T1: ReturnType, T2: E->getType())) {
775 ExprResult CastResult = DeclBuilder.SemaRef.BuildCStyleCastExpr(
776 LParenLoc: SourceLocation(), Ty: AST.getTrivialTypeSourceInfo(T: ReturnType),
777 RParenLoc: SourceLocation(), Op: E);
778 assert(!CastResult.isInvalid() && "Cast cannot fail!");
779 E = CastResult.get();
780 }
781
782 StmtsList.push_back(Elt: E);
783 return *this;
784}
785
786template <typename TLHS, typename TRHS>
787BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::assign(TLHS LHS, TRHS RHS) {
788 Expr *LHSExpr = convertPlaceholder(LHS);
789 Expr *RHSExpr = convertPlaceholder(RHS);
790 Stmt *AssignStmt = BinaryOperator::Create(
791 C: getASTContext(), lhs: LHSExpr, rhs: RHSExpr, opc: BO_Assign, ResTy: LHSExpr->getType(),
792 VK: ExprValueKind::VK_PRValue, OK: ExprObjectKind::OK_Ordinary, opLoc: SourceLocation(),
793 FPFeatures: FPOptionsOverride());
794 StmtsList.push_back(Elt: AssignStmt);
795 return *this;
796}
797
798template <typename T>
799BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::dereference(T Ptr) {
800 Expr *PtrExpr = convertPlaceholder(Ptr);
801 Expr *Deref = UnaryOperator::Create(
802 C: getASTContext(), input: PtrExpr, opc: UO_Deref, type: PtrExpr->getType()->getPointeeType(),
803 VK: VK_LValue, OK: OK_Ordinary, l: SourceLocation(),
804 /*CanOverflow=*/false, FPFeatures: FPOptionsOverride());
805 StmtsList.push_back(Elt: Deref);
806 return *this;
807}
808
809template <typename T>
810BuiltinTypeMethodBuilder &
811BuiltinTypeMethodBuilder::accessHandleFieldOnResource(T ResourceRecord) {
812 ensureCompleteDecl();
813
814 Expr *ResourceExpr = convertPlaceholder(ResourceRecord);
815 auto *ResourceTypeDecl = ResourceExpr->getType()->getAsCXXRecordDecl();
816
817 ASTContext &AST = getASTContext();
818 FieldDecl *HandleField = nullptr;
819
820 if (ResourceTypeDecl == DeclBuilder.Record)
821 HandleField = DeclBuilder.getResourceHandleField();
822 else {
823 IdentifierInfo &II = AST.Idents.get(Name: "__handle");
824 for (auto *Decl : ResourceTypeDecl->lookup(Name: &II)) {
825 if ((HandleField = dyn_cast<FieldDecl>(Val: Decl)))
826 break;
827 }
828 assert(HandleField && "Resource handle field not found");
829 }
830
831 MemberExpr *HandleExpr = MemberExpr::CreateImplicit(
832 C: AST, Base: ResourceExpr, IsArrow: false, MemberDecl: HandleField, T: HandleField->getType(), VK: VK_LValue,
833 OK: OK_Ordinary);
834 StmtsList.push_back(Elt: HandleExpr);
835 return *this;
836}
837
838template <typename T>
839BuiltinTypeMethodBuilder &
840BuiltinTypeMethodBuilder::accessFieldOnResource(T ResourceRecord,
841 FieldDecl *Field) {
842 ensureCompleteDecl();
843 auto *Member = createMemberExpr(ResourceRecord, Field);
844 StmtsList.push_back(Elt: Member);
845 return *this;
846}
847
848void BuiltinTypeMethodBuilder::setMipsHandleField(LocalVar &ResourceRecord) {
849 FieldDecl *MipsField = DeclBuilder.Fields.lookup(Key: "mips");
850 if (!MipsField)
851 return;
852
853 QualType MipsTy = MipsField->getType();
854 const auto *RT = MipsTy->castAs<RecordType>();
855 CXXRecordDecl *MipsRecord = cast<CXXRecordDecl>(Val: RT->getDecl());
856
857 // The mips record should have a single field that is the handle.
858 assert(MipsRecord->field_begin() != MipsRecord->field_end() &&
859 "mips_type must have at least one field");
860 assert(std::next(MipsRecord->field_begin()) == MipsRecord->field_end() &&
861 "mips_type must have exactly one field");
862 FieldDecl *MipsHandleField = *MipsRecord->field_begin();
863
864 FieldDecl *HandleField = DeclBuilder.getResourceHandleField();
865 Expr *ResExpr = convertPlaceholder(Var&: ResourceRecord);
866 MemberExpr *HandleMemberExpr = createMemberExpr(Base: ResExpr, Member: HandleField);
867
868 MemberExpr *MipsMemberExpr = createMemberExpr(Base: ResExpr, Member: MipsField);
869 MemberExpr *MipsHandleMemberExpr =
870 createMemberExpr(Base: MipsMemberExpr, Member: MipsHandleField);
871
872 Stmt *AssignStmt = BinaryOperator::Create(
873 C: getASTContext(), lhs: MipsHandleMemberExpr, rhs: HandleMemberExpr, opc: BO_Assign,
874 ResTy: MipsHandleMemberExpr->getType(), VK: ExprValueKind::VK_LValue,
875 OK: ExprObjectKind::OK_Ordinary, opLoc: SourceLocation(), FPFeatures: FPOptionsOverride());
876
877 StmtsList.push_back(Elt: AssignStmt);
878}
879
880template <typename ValueT>
881BuiltinTypeMethodBuilder &
882BuiltinTypeMethodBuilder::setHandleFieldOnResource(LocalVar &ResourceRecord,
883 ValueT HandleValue) {
884 setFieldOnResource(ResourceRecord, HandleValue,
885 DeclBuilder.getResourceHandleField());
886 setMipsHandleField(ResourceRecord);
887 return *this;
888}
889
890template <typename ResourceT, typename ValueT>
891BuiltinTypeMethodBuilder &
892BuiltinTypeMethodBuilder::setCounterHandleFieldOnResource(
893 ResourceT ResourceRecord, ValueT HandleValue) {
894 return setFieldOnResource(ResourceRecord, HandleValue,
895 DeclBuilder.getResourceCounterHandleField());
896}
897
898template <typename ResourceT, typename ValueT>
899BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::setFieldOnResource(
900 ResourceT ResourceRecord, ValueT HandleValue, FieldDecl *HandleField) {
901 ensureCompleteDecl();
902
903 Expr *ResourceExpr = convertPlaceholder(ResourceRecord);
904 assert(ResourceExpr->getType()->getAsCXXRecordDecl() ==
905 HandleField->getParent() &&
906 "Getting the field from the wrong resource type.");
907
908 Expr *HandleValueExpr = convertPlaceholder(HandleValue);
909
910 MemberExpr *HandleMemberExpr = createMemberExpr(Base: ResourceExpr, Member: HandleField);
911 Stmt *AssignStmt = BinaryOperator::Create(
912 C: getASTContext(), lhs: HandleMemberExpr, rhs: HandleValueExpr, opc: BO_Assign,
913 ResTy: HandleMemberExpr->getType(), VK: ExprValueKind::VK_PRValue,
914 OK: ExprObjectKind::OK_Ordinary, opLoc: SourceLocation(), FPFeatures: FPOptionsOverride());
915 StmtsList.push_back(Elt: AssignStmt);
916 return *this;
917}
918
919template <typename T>
920BuiltinTypeMethodBuilder &
921BuiltinTypeMethodBuilder::accessCounterHandleFieldOnResource(T ResourceRecord) {
922 ensureCompleteDecl();
923
924 Expr *ResourceExpr = convertPlaceholder(ResourceRecord);
925 assert(ResourceExpr->getType()->getAsCXXRecordDecl() == DeclBuilder.Record &&
926 "Getting the field from the wrong resource type.");
927
928 FieldDecl *HandleField = DeclBuilder.getResourceCounterHandleField();
929 MemberExpr *HandleExpr = createMemberExpr(Base: ResourceExpr, Member: HandleField);
930 StmtsList.push_back(Elt: HandleExpr);
931 return *this;
932}
933
934template <typename T>
935BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::returnValue(T ReturnValue) {
936 ensureCompleteDecl();
937
938 Expr *ReturnValueExpr = convertPlaceholder(ReturnValue);
939 ASTContext &AST = getASTContext();
940
941 QualType Ty = ReturnValueExpr->getType();
942 if (Ty->isRecordType() && !Method->getReturnType()->isReferenceType()) {
943 // For record types, create a call to copy constructor to ensure proper copy
944 // semantics.
945 auto *ICE =
946 ImplicitCastExpr::Create(Context: AST, T: Ty.withConst(), Kind: CK_NoOp, Operand: ReturnValueExpr,
947 BasePath: nullptr, Cat: VK_XValue, FPO: FPOptionsOverride());
948 CXXConstructorDecl *CD = lookupCopyConstructor(ResTy: Ty);
949 assert(CD && "no copy constructor found");
950 ReturnValueExpr = CXXConstructExpr::Create(
951 Ctx: AST, Ty, Loc: SourceLocation(), Ctor: CD, /*Elidable=*/false, Args: {ICE},
952 /*HadMultipleCandidates=*/false, /*ListInitialization=*/false,
953 /*StdInitListInitialization=*/false,
954 /*ZeroInitListInitialization=*/ZeroInitialization: false, ConstructKind: CXXConstructionKind::Complete,
955 ParenOrBraceRange: SourceRange());
956 }
957 StmtsList.push_back(
958 Elt: ReturnStmt::Create(Ctx: AST, RL: SourceLocation(), E: ReturnValueExpr, NRVOCandidate: nullptr));
959 return *this;
960}
961
962BuiltinTypeDeclBuilder &
963BuiltinTypeMethodBuilder::finalize(AccessSpecifier Access) {
964 assert(!DeclBuilder.Record->isCompleteDefinition() &&
965 "record is already complete");
966
967 ensureCompleteDecl();
968
969 if (!Method->hasBody()) {
970 ASTContext &AST = getASTContext();
971 assert((ReturnTy == AST.VoidTy || !StmtsList.empty()) &&
972 "nothing to return from non-void method");
973 if (ReturnTy != AST.VoidTy) {
974 if (Expr *LastExpr = dyn_cast<Expr>(Val: StmtsList.back())) {
975 assert(AST.hasSameUnqualifiedType(LastExpr->getType(),
976 ReturnTy.getNonReferenceType()) &&
977 "Return type of the last statement must match the return type "
978 "of the method");
979 if (!isa<ReturnStmt>(Val: LastExpr)) {
980 StmtsList.pop_back();
981 StmtsList.push_back(
982 Elt: ReturnStmt::Create(Ctx: AST, RL: SourceLocation(), E: LastExpr, NRVOCandidate: nullptr));
983 }
984 }
985 }
986
987 Method->setBody(CompoundStmt::Create(C: AST, Stmts: StmtsList, FPFeatures: FPOptionsOverride(),
988 LB: SourceLocation(), RB: SourceLocation()));
989 Method->setLexicalDeclContext(DeclBuilder.Record);
990 Method->setAccess(Access);
991 Method->setImplicitlyInline();
992 Method->addAttr(A: AlwaysInlineAttr::CreateImplicit(
993 Ctx&: AST, Range: SourceRange(), S: AlwaysInlineAttr::CXX11_clang_always_inline));
994 Method->addAttr(A: ConvergentAttr::CreateImplicit(Ctx&: AST));
995 if (!TemplateParamDecls.empty()) {
996 TemplateParams = TemplateParameterList::Create(
997 C: AST, TemplateLoc: SourceLocation(), LAngleLoc: SourceLocation(), Params: TemplateParamDecls,
998 RAngleLoc: SourceLocation(), RequiresClause: nullptr);
999
1000 auto *FuncTemplate = FunctionTemplateDecl::Create(C&: AST, DC: DeclBuilder.Record,
1001 L: SourceLocation(), Name,
1002 Params: TemplateParams, Decl: Method);
1003 FuncTemplate->setAccess(AS_public);
1004 FuncTemplate->setLexicalDeclContext(DeclBuilder.Record);
1005 FuncTemplate->setImplicit(true);
1006 Method->setDescribedFunctionTemplate(FuncTemplate);
1007 DeclBuilder.Record->addDecl(D: FuncTemplate);
1008 } else {
1009 DeclBuilder.Record->addDecl(D: Method);
1010 }
1011 }
1012 return DeclBuilder;
1013}
1014
1015BuiltinTypeDeclBuilder::BuiltinTypeDeclBuilder(Sema &SemaRef, CXXRecordDecl *R)
1016 : SemaRef(SemaRef), Record(R) {
1017 Record->startDefinition();
1018 Template = Record->getDescribedClassTemplate();
1019}
1020
1021BuiltinTypeDeclBuilder::BuiltinTypeDeclBuilder(Sema &SemaRef,
1022 NamespaceDecl *Namespace,
1023 StringRef Name)
1024 : SemaRef(SemaRef), HLSLNamespace(Namespace) {
1025 ASTContext &AST = SemaRef.getASTContext();
1026 IdentifierInfo &II = AST.Idents.get(Name, TokenCode: tok::TokenKind::identifier);
1027
1028 LookupResult Result(SemaRef, &II, SourceLocation(), Sema::LookupTagName);
1029 CXXRecordDecl *PrevDecl = nullptr;
1030 if (SemaRef.LookupQualifiedName(R&: Result, LookupCtx: HLSLNamespace)) {
1031 // Declaration already exists (from precompiled headers)
1032 NamedDecl *Found = Result.getFoundDecl();
1033 if (auto *TD = dyn_cast<ClassTemplateDecl>(Val: Found)) {
1034 PrevDecl = TD->getTemplatedDecl();
1035 PrevTemplate = TD;
1036 } else
1037 PrevDecl = dyn_cast<CXXRecordDecl>(Val: Found);
1038 assert(PrevDecl && "Unexpected lookup result type.");
1039 }
1040
1041 if (PrevDecl && PrevDecl->isCompleteDefinition()) {
1042 Record = PrevDecl;
1043 Template = PrevTemplate;
1044 return;
1045 }
1046
1047 Record =
1048 CXXRecordDecl::Create(C: AST, TK: TagDecl::TagKind::Class, DC: HLSLNamespace,
1049 StartLoc: SourceLocation(), IdLoc: SourceLocation(), Id: &II, PrevDecl);
1050 Record->setImplicit(true);
1051 Record->setLexicalDeclContext(HLSLNamespace);
1052 Record->setHasExternalLexicalStorage();
1053
1054 // Don't let anyone derive from built-in types.
1055 Record->addAttr(
1056 A: FinalAttr::CreateImplicit(Ctx&: AST, Range: SourceRange(), S: FinalAttr::Keyword_final));
1057}
1058
1059BuiltinTypeDeclBuilder::~BuiltinTypeDeclBuilder() {
1060 if (HLSLNamespace && !Template && Record->getDeclContext() == HLSLNamespace)
1061 HLSLNamespace->addDecl(D: Record);
1062}
1063
1064BuiltinTypeDeclBuilder &
1065BuiltinTypeDeclBuilder::addMemberVariable(StringRef Name, QualType Type,
1066 llvm::ArrayRef<Attr *> Attrs,
1067 AccessSpecifier Access) {
1068 assert(!Record->isCompleteDefinition() && "record is already complete");
1069 assert(Record->isBeingDefined() &&
1070 "Definition must be started before adding members!");
1071 ASTContext &AST = Record->getASTContext();
1072
1073 IdentifierInfo &II = AST.Idents.get(Name, TokenCode: tok::TokenKind::identifier);
1074 TypeSourceInfo *MemTySource =
1075 AST.getTrivialTypeSourceInfo(T: Type, Loc: SourceLocation());
1076 auto *Field = FieldDecl::Create(
1077 C: AST, DC: Record, StartLoc: SourceLocation(), IdLoc: SourceLocation(), Id: &II, T: Type, TInfo: MemTySource,
1078 BW: nullptr, Mutable: false, InitStyle: InClassInitStyle::ICIS_NoInit);
1079 Field->setAccess(Access);
1080 Field->setImplicit(true);
1081 for (Attr *A : Attrs) {
1082 if (A)
1083 Field->addAttr(A);
1084 }
1085
1086 Record->addDecl(D: Field);
1087 Fields[Name] = Field;
1088 return *this;
1089}
1090
1091BuiltinTypeDeclBuilder &
1092BuiltinTypeDeclBuilder::addBufferHandles(ResourceClass RC, bool IsROV,
1093 bool RawBuffer, bool HasCounter,
1094 AccessSpecifier Access) {
1095 QualType ElementTy = getHandleElementType();
1096 addHandleMember(RC, RD: ResourceDimension::Unknown, IsROV, RawBuffer,
1097 /*IsArray=*/false, ElementTy, Access);
1098 if (HasCounter)
1099 addCounterHandleMember(RC, IsROV, RawBuffer, ElementTy, Access);
1100 return *this;
1101}
1102
1103BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addTextureHandle(
1104 ResourceClass RC, bool IsROV, bool IsArray, ResourceDimension RD,
1105 Expr *SampleCountExpr, AccessSpecifier Access) {
1106 addResourceMember(MemberName: "__handle", RC, RD, IsROV, /*RawBuffer=*/false,
1107 /*IsCounter=*/false, IsArray, ElementTy: getHandleElementType(),
1108 SampleCountExpr, Access);
1109 return *this;
1110}
1111
1112BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addSamplerHandle() {
1113 addHandleMember(RC: ResourceClass::Sampler, RD: ResourceDimension::Unknown,
1114 /*IsROV=*/false, /*RawBuffer=*/false, /*IsArray=*/false,
1115 ElementTy: getHandleElementType());
1116 return *this;
1117}
1118
1119BuiltinTypeDeclBuilder &
1120BuiltinTypeDeclBuilder::addConstantBufferConversionToType() {
1121 assert(!Record->isCompleteDefinition() && "record is already complete");
1122 ASTContext &AST = SemaRef.getASTContext();
1123 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1124
1125 QualType ElemTy = getHandleElementType();
1126 QualType AddrSpaceElemTy = AST.getCanonicalType(
1127 T: AST.getAddrSpaceQualType(T: ElemTy.withConst(), AddressSpace: LangAS::hlsl_constant));
1128 QualType ReturnTy =
1129 AST.getCanonicalType(T: AST.getLValueReferenceType(T: AddrSpaceElemTy));
1130
1131 DeclarationName Name = AST.DeclarationNames.getCXXConversionFunctionName(
1132 Ty: AST.getCanonicalType(T: ReturnTy));
1133
1134 return BuiltinTypeMethodBuilder(*this, Name, ReturnTy, /*IsConst=*/true)
1135 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_getpointer",
1136 ReturnType: AST.getPointerType(T: AddrSpaceElemTy), ArgSpecs: PH::Handle)
1137 .dereference(Ptr: PH::LastStmt)
1138 .finalize();
1139}
1140
1141BuiltinTypeDeclBuilder &
1142BuiltinTypeDeclBuilder::addFriend(CXXRecordDecl *Friend) {
1143 assert(!Record->isCompleteDefinition() && "record is already complete");
1144 ASTContext &AST = SemaRef.getASTContext();
1145 QualType FriendTy = AST.getCanonicalTagType(TD: Friend);
1146 TypeSourceInfo *TSI = AST.getTrivialTypeSourceInfo(T: FriendTy);
1147 FriendDecl *FD =
1148 FriendDecl::Create(C&: AST, DC: Record, L: SourceLocation(), Friend: TSI, FriendL: SourceLocation());
1149 FD->setAccess(AS_public);
1150 Record->addDecl(D: FD);
1151 return *this;
1152}
1153
1154CXXRecordDecl *BuiltinTypeDeclBuilder::addPrivateNestedRecord(StringRef Name) {
1155 assert(!Record->isCompleteDefinition() && "record is already complete");
1156 ASTContext &AST = SemaRef.getASTContext();
1157 IdentifierInfo &II = AST.Idents.get(Name, TokenCode: tok::TokenKind::identifier);
1158 CXXRecordDecl *NestedRecord =
1159 CXXRecordDecl::Create(C: AST, TK: TagDecl::TagKind::Struct, DC: Record,
1160 StartLoc: SourceLocation(), IdLoc: SourceLocation(), Id: &II);
1161 NestedRecord->setImplicit(true);
1162 NestedRecord->setAccess(AccessSpecifier::AS_private);
1163 NestedRecord->setLexicalDeclContext(Record);
1164 Record->addDecl(D: NestedRecord);
1165 return NestedRecord;
1166}
1167
1168BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleMember(
1169 ResourceClass RC, ResourceDimension RD, bool IsROV, bool RawBuffer,
1170 bool IsArray, QualType ElementTy, AccessSpecifier Access) {
1171 return addResourceMember(MemberName: "__handle", RC, RD, IsROV, RawBuffer,
1172 /*IsCounter=*/false, IsArray, ElementTy,
1173 /*SampleCountExpr=*/nullptr, Access);
1174}
1175
1176BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCounterHandleMember(
1177 ResourceClass RC, bool IsROV, bool RawBuffer, QualType ElementTy,
1178 AccessSpecifier Access) {
1179 return addResourceMember(MemberName: "__counter_handle", RC, RD: ResourceDimension::Unknown,
1180 IsROV, RawBuffer, /*IsCounter=*/true,
1181 /*IsArray=*/false, ElementTy,
1182 /*SampleCountExpr=*/nullptr, Access);
1183}
1184
1185BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addResourceMember(
1186 StringRef MemberName, ResourceClass RC, ResourceDimension RD, bool IsROV,
1187 bool RawBuffer, bool IsCounter, bool IsArray, QualType ElementTy,
1188 Expr *SampleCountExpr, AccessSpecifier Access) {
1189 assert(!Record->isCompleteDefinition() && "record is already complete");
1190
1191 ASTContext &AST = SemaRef.getASTContext();
1192
1193 assert(!ElementTy.isNull() &&
1194 "The caller should always pass in the type for the handle.");
1195 TypeSourceInfo *ElementTypeInfo =
1196 AST.getTrivialTypeSourceInfo(T: ElementTy, Loc: SourceLocation());
1197
1198 // add handle member with resource type attributes
1199 QualType AttributedResTy = QualType();
1200 SmallVector<const Attr *> Attrs = {
1201 HLSLResourceClassAttr::CreateImplicit(Ctx&: AST, ResourceClass: RC),
1202 IsROV ? HLSLIsROVAttr::CreateImplicit(Ctx&: AST) : nullptr,
1203 RawBuffer ? HLSLRawBufferAttr::CreateImplicit(Ctx&: AST) : nullptr,
1204 RD != ResourceDimension::Unknown
1205 ? HLSLResourceDimensionAttr::CreateImplicit(Ctx&: AST, Dimension: RD)
1206 : nullptr,
1207 ElementTypeInfo && RC != ResourceClass::Sampler
1208 ? HLSLContainedTypeAttr::CreateImplicit(Ctx&: AST, Type: ElementTypeInfo)
1209 : nullptr};
1210 if (IsCounter)
1211 Attrs.push_back(Elt: HLSLIsCounterAttr::CreateImplicit(Ctx&: AST));
1212 if (IsArray)
1213 Attrs.push_back(Elt: HLSLIsArrayAttr::CreateImplicit(Ctx&: AST));
1214 if (SampleCountExpr)
1215 Attrs.push_back(Elt: HLSLIsMultiSampledAttr::CreateImplicit(Ctx&: AST));
1216
1217 if (CreateHLSLAttributedResourceType(S&: SemaRef, Wrapped: AST.HLSLResourceTy, AttrList: Attrs,
1218 ResType&: AttributedResTy, /*LocInfo=*/nullptr,
1219 SampleCountExpr))
1220 addMemberVariable(Name: MemberName, Type: AttributedResTy, Attrs: {}, Access);
1221 return *this;
1222}
1223
1224// Adds default constructor to the resource class:
1225// Resource::Resource()
1226BuiltinTypeDeclBuilder &
1227BuiltinTypeDeclBuilder::addDefaultHandleConstructor(AccessSpecifier Access) {
1228 assert(!Record->isCompleteDefinition() && "record is already complete");
1229
1230 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1231 QualType HandleType = getResourceHandleField()->getType();
1232 return BuiltinTypeMethodBuilder(*this, "", SemaRef.getASTContext().VoidTy,
1233 false, true)
1234 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_uninitializedhandle", ReturnType: HandleType,
1235 ArgSpecs: PH::Handle)
1236 .assign(LHS: PH::Handle, RHS: PH::LastStmt)
1237 .finalize(Access);
1238}
1239
1240// Adds constructor that takes hlsl::__detail::heap_resource_info:
1241// Resource::Resource(hlsl::__detail::heap_resource_info info) {
1242// __handle = __builtin_hlsl_resource_handlefromheap(__handle, info.Index);
1243// }
1244BuiltinTypeDeclBuilder &
1245BuiltinTypeDeclBuilder::addHeapResourceInfoConstructor(bool HasCounter) {
1246 assert(!Record->isCompleteDefinition() && "record is already complete");
1247
1248 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1249
1250 ASTContext &AST = SemaRef.getASTContext();
1251 QualType HandleType = getResourceHandleField()->getType();
1252
1253 NamespaceDecl *HLSLDetailNS =
1254 lookupBuiltinNamespace(S&: SemaRef, Name: "__detail", DC: Record->getDeclContext());
1255 QualType HeapResInfoType =
1256 lookupBuiltinType(S&: SemaRef, Name: "heap_resource_info", DC: HLSLDetailNS);
1257 CXXRecordDecl *HeapResInfoDecl = HeapResInfoType->getAsCXXRecordDecl();
1258
1259 FieldDecl *IndexField = *HeapResInfoDecl->field_begin();
1260 assert(IndexField && IndexField->getType() == AST.UnsignedIntTy &&
1261 "Index field not as expected");
1262
1263 auto MB = BuiltinTypeMethodBuilder(*this, "", AST.VoidTy, false, true);
1264 MB.addParam(Name: "HeapResInfo", Ty: HeapResInfoType)
1265 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_handlefromheap", ReturnType: HandleType,
1266 ArgSpecs: PH::Handle, ArgSpecs: MB.createMemberExpr(Base: PH::_0, Member: IndexField))
1267 .assign(LHS: PH::Handle, RHS: PH::LastStmt);
1268
1269 if (HasCounter) {
1270 QualType CounterHandleType = getResourceCounterHandleField()->getType();
1271 MB.callBuiltin(BuiltinName: "__builtin_hlsl_resource_counterhandlefromheap",
1272 ReturnType: CounterHandleType, ArgSpecs: PH::Handle)
1273 .assign(LHS: PH::CounterHandle, RHS: PH::LastStmt);
1274 }
1275
1276 return MB.finalize();
1277}
1278
1279// Adds constructor that takes hlsl::__detail::heap_sampler_info:
1280// Resource::Resource(hlsl::__detail::heap_sampler_info info) {
1281// __handle = __builtin_hlsl_resource_handlefromheap(__handle, info.Index);
1282// }
1283BuiltinTypeDeclBuilder &
1284BuiltinTypeDeclBuilder::addHeapSamplerInfoConstructor() {
1285 assert(!Record->isCompleteDefinition() && "record is already complete");
1286
1287 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1288
1289 ASTContext &AST = SemaRef.getASTContext();
1290 QualType HandleType = getResourceHandleField()->getType();
1291
1292 NamespaceDecl *HLSLDetailNS =
1293 lookupBuiltinNamespace(S&: SemaRef, Name: "__detail", DC: Record->getDeclContext());
1294 QualType HeapResInfoType =
1295 lookupBuiltinType(S&: SemaRef, Name: "heap_sampler_info", DC: HLSLDetailNS);
1296 CXXRecordDecl *HeapResInfoDecl = HeapResInfoType->getAsCXXRecordDecl();
1297
1298 FieldDecl *IndexField = *HeapResInfoDecl->field_begin();
1299 assert(IndexField && IndexField->getType() == AST.UnsignedIntTy &&
1300 "Index field not as expected");
1301
1302 auto MB = BuiltinTypeMethodBuilder(*this, "", AST.VoidTy, false, true);
1303 MB.addParam(Name: "HeapResInfo", Ty: HeapResInfoType);
1304 MB.callBuiltin(BuiltinName: "__builtin_hlsl_resource_handlefromheap", ReturnType: HandleType,
1305 ArgSpecs: PH::Handle, ArgSpecs: MB.createMemberExpr(Base: PH::_0, Member: IndexField))
1306 .assign(LHS: PH::Handle, RHS: PH::LastStmt);
1307
1308 return MB.finalize();
1309}
1310
1311BuiltinTypeDeclBuilder &
1312BuiltinTypeDeclBuilder::addStaticInitializationFunctions(bool HasCounter) {
1313 if (HasCounter) {
1314 addCreateFromBindingWithImplicitCounter();
1315 addCreateFromImplicitBindingWithImplicitCounter();
1316 } else {
1317 addCreateFromBinding();
1318 addCreateFromImplicitBinding();
1319 }
1320 return *this;
1321}
1322
1323// Adds static method that initializes resource from binding:
1324//
1325// static Resource<T> __createFromBinding(unsigned registerNo,
1326// unsigned spaceNo, int range,
1327// unsigned index, const char *name) {
1328// Resource<T> tmp;
1329// tmp.__handle = __builtin_hlsl_resource_handlefrombinding(
1330// tmp.__handle, registerNo, spaceNo,
1331// range, index, name);
1332// return tmp;
1333// }
1334BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCreateFromBinding() {
1335 assert(!Record->isCompleteDefinition() && "record is already complete");
1336
1337 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1338 ASTContext &AST = SemaRef.getASTContext();
1339 QualType HandleType = getResourceHandleField()->getType();
1340 QualType RecordType = AST.getTypeDeclType(Decl: cast<TypeDecl>(Val: Record));
1341 BuiltinTypeMethodBuilder::LocalVar TmpVar("tmp", RecordType);
1342
1343 return BuiltinTypeMethodBuilder(*this, "__createFromBinding", RecordType,
1344 false, false, SC_Static)
1345 .addParam(Name: "registerNo", Ty: AST.UnsignedIntTy)
1346 .addParam(Name: "spaceNo", Ty: AST.UnsignedIntTy)
1347 .addParam(Name: "range", Ty: AST.IntTy)
1348 .addParam(Name: "index", Ty: AST.UnsignedIntTy)
1349 .addParam(Name: "name", Ty: AST.getPointerType(T: AST.CharTy.withConst()))
1350 .declareLocalVar(Var&: TmpVar)
1351 .accessHandleFieldOnResource(ResourceRecord: TmpVar)
1352 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_handlefrombinding", ReturnType: HandleType,
1353 ArgSpecs: PH::LastStmt, ArgSpecs: PH::_0, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3, ArgSpecs: PH::_4)
1354 .setHandleFieldOnResource(ResourceRecord&: TmpVar, HandleValue: PH::LastStmt)
1355 .returnValue(ReturnValue: TmpVar)
1356 .finalize();
1357}
1358
1359// Adds static method that initializes resource from binding:
1360//
1361// static Resource<T> __createFromImplicitBinding(unsigned orderId,
1362// unsigned spaceNo, int range,
1363// unsigned index,
1364// const char *name) {
1365// Resource<T> tmp;
1366// tmp.__handle = __builtin_hlsl_resource_handlefromimplicitbinding(
1367// tmp.__handle, spaceNo,
1368// range, index, orderId, name);
1369// return tmp;
1370// }
1371BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCreateFromImplicitBinding() {
1372 assert(!Record->isCompleteDefinition() && "record is already complete");
1373
1374 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1375 ASTContext &AST = SemaRef.getASTContext();
1376 QualType HandleType = getResourceHandleField()->getType();
1377 QualType RecordType = AST.getTypeDeclType(Decl: cast<TypeDecl>(Val: Record));
1378 BuiltinTypeMethodBuilder::LocalVar TmpVar("tmp", RecordType);
1379
1380 return BuiltinTypeMethodBuilder(*this, "__createFromImplicitBinding",
1381 RecordType, false, false, SC_Static)
1382 .addParam(Name: "orderId", Ty: AST.UnsignedIntTy)
1383 .addParam(Name: "spaceNo", Ty: AST.UnsignedIntTy)
1384 .addParam(Name: "range", Ty: AST.IntTy)
1385 .addParam(Name: "index", Ty: AST.UnsignedIntTy)
1386 .addParam(Name: "name", Ty: AST.getPointerType(T: AST.CharTy.withConst()))
1387 .declareLocalVar(Var&: TmpVar)
1388 .accessHandleFieldOnResource(ResourceRecord: TmpVar)
1389 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_handlefromimplicitbinding",
1390 ReturnType: HandleType, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_0, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3,
1391 ArgSpecs: PH::_4)
1392 .setHandleFieldOnResource(ResourceRecord&: TmpVar, HandleValue: PH::LastStmt)
1393 .returnValue(ReturnValue: TmpVar)
1394 .finalize();
1395}
1396
1397// Adds static method that initializes resource from binding:
1398//
1399// static Resource<T>
1400// __createFromBindingWithImplicitCounter(unsigned registerNo,
1401// unsigned spaceNo, int range,
1402// unsigned index, const char *name,
1403// unsigned counterOrderId) {
1404// Resource<T> tmp;
1405// tmp.__handle = __builtin_hlsl_resource_handlefrombinding(
1406// tmp.__handle, registerNo, spaceNo, range, index, name);
1407// tmp.__counter_handle =
1408// __builtin_hlsl_resource_counterhandlefromimplicitbinding(
1409// tmp.__handle, counterOrderId, spaceNo);
1410// return tmp;
1411// }
1412BuiltinTypeDeclBuilder &
1413BuiltinTypeDeclBuilder::addCreateFromBindingWithImplicitCounter() {
1414 assert(!Record->isCompleteDefinition() && "record is already complete");
1415
1416 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1417 ASTContext &AST = SemaRef.getASTContext();
1418 QualType HandleType = getResourceHandleField()->getType();
1419 QualType CounterHandleType = getResourceCounterHandleField()->getType();
1420 QualType RecordType = AST.getTypeDeclType(Decl: cast<TypeDecl>(Val: Record));
1421 BuiltinTypeMethodBuilder::LocalVar TmpVar("tmp", RecordType);
1422
1423 return BuiltinTypeMethodBuilder(*this,
1424 "__createFromBindingWithImplicitCounter",
1425 RecordType, false, false, SC_Static)
1426 .addParam(Name: "registerNo", Ty: AST.UnsignedIntTy)
1427 .addParam(Name: "spaceNo", Ty: AST.UnsignedIntTy)
1428 .addParam(Name: "range", Ty: AST.IntTy)
1429 .addParam(Name: "index", Ty: AST.UnsignedIntTy)
1430 .addParam(Name: "name", Ty: AST.getPointerType(T: AST.CharTy.withConst()))
1431 .addParam(Name: "counterOrderId", Ty: AST.UnsignedIntTy)
1432 .declareLocalVar(Var&: TmpVar)
1433 .accessHandleFieldOnResource(ResourceRecord: TmpVar)
1434 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_handlefrombinding", ReturnType: HandleType,
1435 ArgSpecs: PH::LastStmt, ArgSpecs: PH::_0, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3, ArgSpecs: PH::_4)
1436 .setHandleFieldOnResource(ResourceRecord&: TmpVar, HandleValue: PH::LastStmt)
1437 .accessHandleFieldOnResource(ResourceRecord: TmpVar)
1438 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_counterhandlefromimplicitbinding",
1439 ReturnType: CounterHandleType, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_5, ArgSpecs: PH::_1)
1440 .setCounterHandleFieldOnResource(ResourceRecord: TmpVar, HandleValue: PH::LastStmt)
1441 .returnValue(ReturnValue: TmpVar)
1442 .finalize();
1443}
1444
1445// Adds static method that initializes resource from binding:
1446//
1447// static Resource<T>
1448// __createFromImplicitBindingWithImplicitCounter(unsigned orderId,
1449// unsigned spaceNo, int range,
1450// unsigned index,
1451// const char *name,
1452// unsigned counterOrderId) {
1453// Resource<T> tmp;
1454// tmp.__handle = __builtin_hlsl_resource_handlefromimplicitbinding(
1455// tmp.__handle, orderId, spaceNo, range, index, name);
1456// tmp.__counter_handle =
1457// __builtin_hlsl_resource_counterhandlefromimplicitbinding(
1458// tmp.__handle, counterOrderId, spaceNo);
1459// return tmp;
1460// }
1461BuiltinTypeDeclBuilder &
1462BuiltinTypeDeclBuilder::addCreateFromImplicitBindingWithImplicitCounter() {
1463 assert(!Record->isCompleteDefinition() && "record is already complete");
1464
1465 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1466 ASTContext &AST = SemaRef.getASTContext();
1467 QualType HandleType = getResourceHandleField()->getType();
1468 QualType CounterHandleType = getResourceCounterHandleField()->getType();
1469 QualType RecordType = AST.getTypeDeclType(Decl: cast<TypeDecl>(Val: Record));
1470 BuiltinTypeMethodBuilder::LocalVar TmpVar("tmp", RecordType);
1471
1472 return BuiltinTypeMethodBuilder(
1473 *this, "__createFromImplicitBindingWithImplicitCounter",
1474 RecordType, false, false, SC_Static)
1475 .addParam(Name: "orderId", Ty: AST.UnsignedIntTy)
1476 .addParam(Name: "spaceNo", Ty: AST.UnsignedIntTy)
1477 .addParam(Name: "range", Ty: AST.IntTy)
1478 .addParam(Name: "index", Ty: AST.UnsignedIntTy)
1479 .addParam(Name: "name", Ty: AST.getPointerType(T: AST.CharTy.withConst()))
1480 .addParam(Name: "counterOrderId", Ty: AST.UnsignedIntTy)
1481 .declareLocalVar(Var&: TmpVar)
1482 .accessHandleFieldOnResource(ResourceRecord: TmpVar)
1483 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_handlefromimplicitbinding",
1484 ReturnType: HandleType, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_0, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3,
1485 ArgSpecs: PH::_4)
1486 .setHandleFieldOnResource(ResourceRecord&: TmpVar, HandleValue: PH::LastStmt)
1487 .accessHandleFieldOnResource(ResourceRecord: TmpVar)
1488 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_counterhandlefromimplicitbinding",
1489 ReturnType: CounterHandleType, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_5, ArgSpecs: PH::_1)
1490 .setCounterHandleFieldOnResource(ResourceRecord: TmpVar, HandleValue: PH::LastStmt)
1491 .returnValue(ReturnValue: TmpVar)
1492 .finalize();
1493}
1494
1495BuiltinTypeDeclBuilder &
1496BuiltinTypeDeclBuilder::addCopyConstructor(AccessSpecifier Access) {
1497 assert(!Record->isCompleteDefinition() && "record is already complete");
1498
1499 ASTContext &AST = SemaRef.getASTContext();
1500 QualType RecordType = AST.getCanonicalTagType(TD: Record);
1501 QualType ConstRecordType = RecordType.withConst();
1502 QualType ConstRecordRefType = AST.getLValueReferenceType(T: ConstRecordType);
1503
1504 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1505
1506 BuiltinTypeMethodBuilder MMB(*this, /*Name=*/"", AST.VoidTy,
1507 /*IsConst=*/false, /*IsCtor=*/true);
1508 MMB.addParam(Name: "other", Ty: ConstRecordRefType);
1509
1510 for (auto *Field : Record->fields()) {
1511 MMB.accessFieldOnResource(ResourceRecord: PH::_0, Field)
1512 .setFieldOnResource(ResourceRecord: PH::This, HandleValue: PH::LastStmt, HandleField: Field);
1513 }
1514
1515 return MMB.finalize(Access);
1516}
1517
1518BuiltinTypeDeclBuilder &
1519BuiltinTypeDeclBuilder::addCopyAssignmentOperator(AccessSpecifier Access) {
1520 assert(!Record->isCompleteDefinition() && "record is already complete");
1521
1522 ASTContext &AST = SemaRef.getASTContext();
1523 QualType RecordType = AST.getCanonicalTagType(TD: Record);
1524 QualType ConstRecordType = RecordType.withConst();
1525 QualType ConstRecordRefType = AST.getLValueReferenceType(T: ConstRecordType);
1526 QualType RecordRefType = AST.getLValueReferenceType(T: RecordType);
1527
1528 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1529 DeclarationName Name = AST.DeclarationNames.getCXXOperatorName(Op: OO_Equal);
1530 BuiltinTypeMethodBuilder MMB(*this, Name, RecordRefType);
1531 MMB.addParam(Name: "other", Ty: ConstRecordRefType);
1532
1533 for (auto *Field : Record->fields()) {
1534 MMB.accessFieldOnResource(ResourceRecord: PH::_0, Field)
1535 .setFieldOnResource(ResourceRecord: PH::This, HandleValue: PH::LastStmt, HandleField: Field);
1536 }
1537
1538 return MMB.returnThis().finalize(Access);
1539}
1540
1541BuiltinTypeDeclBuilder &
1542BuiltinTypeDeclBuilder::addArraySubscriptOperators(ResourceDimension Dim,
1543 bool IsArray) {
1544 assert(!Record->isCompleteDefinition() && "record is already complete");
1545 ASTContext &AST = Record->getASTContext();
1546
1547 uint32_t VecSize = 1;
1548 if (Dim != ResourceDimension::Unknown)
1549 VecSize = getResourceDimensions(Dim) + (IsArray ? 1 : 0);
1550
1551 QualType IndexTy = getVectorOrScalarType(AST, Ty: AST.UnsignedIntTy, NumElements: VecSize);
1552
1553 DeclarationName Subscript =
1554 AST.DeclarationNames.getCXXOperatorName(Op: OO_Subscript);
1555
1556 addHandleAccessFunction(Name&: Subscript,
1557 /*IsConstReturn=*/getResourceAttrs().ResourceClass !=
1558 llvm::dxil::ResourceClass::UAV,
1559 /*IsRef=*/true, IndexTy);
1560
1561 return *this;
1562}
1563
1564BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addLoadMethods() {
1565 assert(!Record->isCompleteDefinition() && "record is already complete");
1566
1567 ASTContext &AST = Record->getASTContext();
1568 IdentifierInfo &II = AST.Idents.get(Name: "Load", TokenCode: tok::TokenKind::identifier);
1569 DeclarationName Load(&II);
1570
1571 addHandleAccessFunction(Name&: Load,
1572 /*IsConstReturn=*/false, /*IsRef=*/false,
1573 IndexTy: AST.UnsignedIntTy);
1574 addLoadWithStatusFunction(Name&: Load);
1575
1576 return *this;
1577}
1578
1579CXXRecordDecl *BuiltinTypeDeclBuilder::addMipsSliceType(ResourceDimension Dim,
1580 QualType ReturnType) {
1581 ASTContext &AST = Record->getASTContext();
1582 uint32_t VecSize =
1583 getResourceDimensions(Dim) + (getResourceAttrs().IsArray ? 1 : 0);
1584 QualType IntTy = AST.IntTy;
1585 QualType IndexTy = getVectorOrScalarType(AST, Ty: IntTy, NumElements: VecSize);
1586 QualType CoordLevelTy = AST.getExtVectorType(VectorType: IntTy, NumElts: VecSize + 1);
1587 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1588
1589 // Define the mips_slice_type which is returned by mips_type::operator[].
1590 // It holds the resource handle and the mip level. It has an operator[]
1591 // that takes the coordinate and performs the actual resource load.
1592 CXXRecordDecl *MipsSliceRecord = addPrivateNestedRecord(Name: "mips_slice_type");
1593 BuiltinTypeDeclBuilder MipsSliceBuilder(SemaRef, MipsSliceRecord);
1594 MipsSliceBuilder.addFriend(Friend: Record)
1595 .addHandleMember(RC: getResourceAttrs().ResourceClass, RD: Dim,
1596 IsROV: getResourceAttrs().IsROV, /*RawBuffer=*/false,
1597 IsArray: getResourceAttrs().IsArray, ElementTy: ReturnType,
1598 Access: AccessSpecifier::AS_public)
1599 .addMemberVariable(Name: "__level", Type: IntTy, Attrs: {}, Access: AccessSpecifier::AS_public)
1600 .addDefaultHandleConstructor(Access: AccessSpecifier::AS_protected)
1601 .addCopyConstructor(Access: AccessSpecifier::AS_protected)
1602 .addCopyAssignmentOperator(Access: AccessSpecifier::AS_protected);
1603
1604 FieldDecl *LevelField = MipsSliceBuilder.Fields["__level"];
1605 assert(LevelField && "Could not find the level field.");
1606
1607 DeclarationName SubscriptName =
1608 AST.DeclarationNames.getCXXOperatorName(Op: OO_Subscript);
1609
1610 // operator[](intN coord) on mips_slice_type
1611 BuiltinTypeMethodBuilder(MipsSliceBuilder, SubscriptName, ReturnType,
1612 /*IsConst=*/true)
1613 .addParam(Name: "Coord", Ty: IndexTy)
1614 .accessFieldOnResource(ResourceRecord: PH::This, Field: LevelField)
1615 .concat(Vec: PH::_0, Scalar: PH::LastStmt, ResultTy: CoordLevelTy)
1616 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_load_level", ReturnType, ArgSpecs: PH::Handle,
1617 ArgSpecs: PH::LastStmt)
1618 .finalize();
1619
1620 MipsSliceBuilder.completeDefinition();
1621 return MipsSliceRecord;
1622}
1623
1624CXXRecordDecl *BuiltinTypeDeclBuilder::addMipsType(ResourceDimension Dim,
1625 QualType ReturnType) {
1626 ASTContext &AST = Record->getASTContext();
1627 QualType IntTy = AST.IntTy;
1628 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1629
1630 // First, define the mips_slice_type that will be returned by our operator[].
1631 CXXRecordDecl *MipsSliceRecord = addMipsSliceType(Dim, ReturnType);
1632
1633 // Define the mips_type, which provides the syntax `Resource.mips[level]`.
1634 // It only holds the handle, and its operator[] returns a mips_slice_type
1635 // initialized with the handle and the requested mip level.
1636 CXXRecordDecl *MipsRecord = addPrivateNestedRecord(Name: "mips_type");
1637 BuiltinTypeDeclBuilder MipsBuilder(SemaRef, MipsRecord);
1638 MipsBuilder.addFriend(Friend: Record)
1639 .addHandleMember(RC: getResourceAttrs().ResourceClass, RD: Dim,
1640 IsROV: getResourceAttrs().IsROV, /*RawBuffer=*/false,
1641 IsArray: getResourceAttrs().IsArray, ElementTy: ReturnType,
1642 Access: AccessSpecifier::AS_public)
1643 .addDefaultHandleConstructor(Access: AccessSpecifier::AS_protected)
1644 .addCopyConstructor(Access: AccessSpecifier::AS_protected)
1645 .addCopyAssignmentOperator(Access: AccessSpecifier::AS_protected);
1646
1647 QualType MipsSliceTy = AST.getCanonicalTagType(TD: MipsSliceRecord);
1648
1649 DeclarationName SubscriptName =
1650 AST.DeclarationNames.getCXXOperatorName(Op: OO_Subscript);
1651
1652 // Locate the fields in the slice type so we can initialize them.
1653 auto FieldIt = MipsSliceRecord->field_begin();
1654 FieldDecl *MipsSliceHandleField = *FieldIt;
1655 FieldDecl *LevelField = *++FieldIt;
1656 assert(MipsSliceHandleField->getName() == "__handle" &&
1657 LevelField->getName() == "__level" &&
1658 "Could not find fields on mips_slice_type");
1659
1660 // operator[](int level) on mips_type
1661 BuiltinTypeMethodBuilder::LocalVar MipsSliceVar("slice", MipsSliceTy);
1662 BuiltinTypeMethodBuilder(MipsBuilder, SubscriptName, MipsSliceTy,
1663 /*IsConst=*/true)
1664 .addParam(Name: "Level", Ty: IntTy)
1665 .declareLocalVar(Var&: MipsSliceVar)
1666 .accessHandleFieldOnResource(ResourceRecord: PH::This)
1667 .setFieldOnResource(ResourceRecord: MipsSliceVar, HandleValue: PH::LastStmt, HandleField: MipsSliceHandleField)
1668 .setFieldOnResource(ResourceRecord: MipsSliceVar, HandleValue: PH::_0, HandleField: LevelField)
1669 .returnValue(ReturnValue: MipsSliceVar)
1670 .finalize();
1671
1672 MipsBuilder.completeDefinition();
1673 return MipsRecord;
1674}
1675
1676BuiltinTypeDeclBuilder &
1677BuiltinTypeDeclBuilder::addMipsMember(ResourceDimension Dim) {
1678 assert(!Record->isCompleteDefinition() && "record is already complete");
1679 ASTContext &AST = Record->getASTContext();
1680 QualType ReturnType = getHandleElementType();
1681
1682 CXXRecordDecl *MipsRecord = addMipsType(Dim, ReturnType);
1683
1684 // Add the mips field to the texture
1685 QualType MipsTy = AST.getCanonicalTagType(TD: MipsRecord);
1686 addMemberVariable(Name: "mips", Type: MipsTy, Attrs: {}, Access: AccessSpecifier::AS_public);
1687
1688 return *this;
1689}
1690
1691BuiltinTypeDeclBuilder &
1692BuiltinTypeDeclBuilder::addTextureLoadMethods(ResourceDimension Dim,
1693 bool IsArray) {
1694 assert(!Record->isCompleteDefinition() && "record is already complete");
1695 ASTContext &AST = Record->getASTContext();
1696 uint32_t OffsetSize = getResourceDimensions(Dim);
1697 uint32_t CoordSize = OffsetSize + (IsArray ? 2 : 1);
1698 QualType IntTy = AST.IntTy;
1699 QualType OffsetTy = getVectorOrScalarType(AST, Ty: IntTy, NumElements: OffsetSize);
1700 QualType LocationTy = getVectorOrScalarType(AST, Ty: IntTy, NumElements: CoordSize);
1701 QualType ReturnType = getHandleElementType();
1702
1703 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1704
1705 // T Load(int3 location)
1706 BuiltinTypeMethodBuilder(*this, "Load", ReturnType)
1707 .addParam(Name: "Location", Ty: LocationTy)
1708 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_load_level", ReturnType, ArgSpecs: PH::Handle,
1709 ArgSpecs: PH::_0)
1710 .finalize();
1711
1712 // T Load(int3 location, int2 offset)
1713 BuiltinTypeMethodBuilder(*this, "Load", ReturnType)
1714 .addParam(Name: "Location", Ty: LocationTy)
1715 .addParam(Name: "Offset", Ty: OffsetTy)
1716 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_load_level", ReturnType, ArgSpecs: PH::Handle,
1717 ArgSpecs: PH::_0, ArgSpecs: PH::_1)
1718 .finalize();
1719
1720 return *this;
1721}
1722
1723BuiltinTypeDeclBuilder &
1724BuiltinTypeDeclBuilder::addRWTextureLoadMethods(ResourceDimension Dim,
1725 bool IsArray) {
1726 assert(!Record->isCompleteDefinition() && "record is already complete");
1727
1728 ASTContext &AST = Record->getASTContext();
1729 // A UAV binds a single mip slice: no mip component, no offset overload.
1730 uint32_t CoordSize = getResourceDimensions(Dim) + (IsArray ? 1 : 0);
1731 QualType LocationTy = getVectorOrScalarType(AST, Ty: AST.IntTy, NumElements: CoordSize);
1732 QualType ReturnType = getHandleElementType();
1733
1734 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1735
1736 // T Load(int2 location)
1737 BuiltinTypeMethodBuilder(*this, "Load", ReturnType)
1738 .addParam(Name: "Location", Ty: LocationTy)
1739 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_load_level", ReturnType, ArgSpecs: PH::Handle,
1740 ArgSpecs: PH::_0)
1741 .finalize();
1742
1743 return *this;
1744}
1745
1746BuiltinTypeDeclBuilder &
1747BuiltinTypeDeclBuilder::addTextureLoadMSMethods(ResourceDimension Dim,
1748 bool IsArray) {
1749 assert(!Record->isCompleteDefinition() && "record is already complete");
1750 ASTContext &AST = Record->getASTContext();
1751 uint32_t OffsetSize = getResourceDimensions(Dim);
1752 // Multisampled textures use a plain location (no mip/LOD component).
1753 uint32_t CoordSize = OffsetSize + (IsArray ? 1 : 0);
1754 QualType IntTy = AST.IntTy;
1755 QualType OffsetTy = getVectorOrScalarType(AST, Ty: IntTy, NumElements: OffsetSize);
1756 QualType LocationTy = getVectorOrScalarType(AST, Ty: IntTy, NumElements: CoordSize);
1757 QualType ReturnType = getHandleElementType();
1758
1759 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1760
1761 // T Load(int2 location, int sampleIndex)
1762 BuiltinTypeMethodBuilder(*this, "Load", ReturnType)
1763 .addParam(Name: "Location", Ty: LocationTy)
1764 .addParam(Name: "SampleIndex", Ty: IntTy)
1765 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_load_ms", ReturnType, ArgSpecs: PH::Handle,
1766 ArgSpecs: PH::_0, ArgSpecs: PH::_1)
1767 .finalize();
1768
1769 // T Load(int2 location, int sampleIndex, int2 offset)
1770 BuiltinTypeMethodBuilder(*this, "Load", ReturnType)
1771 .addParam(Name: "Location", Ty: LocationTy)
1772 .addParam(Name: "SampleIndex", Ty: IntTy)
1773 .addParam(Name: "Offset", Ty: OffsetTy)
1774 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_load_ms", ReturnType, ArgSpecs: PH::Handle,
1775 ArgSpecs: PH::_0, ArgSpecs: PH::_1, ArgSpecs: PH::_2)
1776 .finalize();
1777
1778 return *this;
1779}
1780
1781BuiltinTypeDeclBuilder &
1782BuiltinTypeDeclBuilder::addByteAddressBufferLoadMethods() {
1783 assert(!Record->isCompleteDefinition() && "record is already complete");
1784
1785 ASTContext &AST = SemaRef.getASTContext();
1786
1787 auto AddLoads = [&](StringRef MethodName, QualType ReturnType,
1788 bool TransposeResult = false) {
1789 IdentifierInfo &II = AST.Idents.get(Name: MethodName, TokenCode: tok::TokenKind::identifier);
1790 DeclarationName Load(&II);
1791
1792 addHandleAccessFunction(Name&: Load,
1793 /*IsConstReturn=*/false, /*IsRef=*/false,
1794 IndexTy: AST.UnsignedIntTy, ElemTy: ReturnType, TransposeResult);
1795 addLoadWithStatusFunction(Name&: Load, ReturnTy: ReturnType);
1796 };
1797
1798 AddLoads("Load", AST.UnsignedIntTy);
1799 AddLoads("Load2", AST.getExtVectorType(VectorType: AST.UnsignedIntTy, NumElts: 2));
1800 AddLoads("Load3", AST.getExtVectorType(VectorType: AST.UnsignedIntTy, NumElts: 3));
1801 AddLoads("Load4", AST.getExtVectorType(VectorType: AST.UnsignedIntTy, NumElts: 4));
1802
1803 // Templated Load<T>() needs buffer-order-aware handling for matrix T.
1804 AddLoads("Load", AST.DependentTy, /*TransposeResult=*/true);
1805
1806 return *this;
1807}
1808
1809BuiltinTypeDeclBuilder &
1810BuiltinTypeDeclBuilder::addByteAddressBufferStoreMethods() {
1811 assert(!Record->isCompleteDefinition() && "record is already complete");
1812
1813 ASTContext &AST = SemaRef.getASTContext();
1814
1815 auto AddStore = [&](StringRef MethodName, QualType ValueType,
1816 bool TransposeArg = false) {
1817 IdentifierInfo &II = AST.Idents.get(Name: MethodName, TokenCode: tok::TokenKind::identifier);
1818 DeclarationName Store(&II);
1819
1820 addStoreFunction(Name&: Store, /*IsConst=*/false, ValueType, TransposeArg);
1821 };
1822
1823 AddStore("Store", AST.UnsignedIntTy);
1824 AddStore("Store2", AST.getExtVectorType(VectorType: AST.UnsignedIntTy, NumElts: 2));
1825 AddStore("Store3", AST.getExtVectorType(VectorType: AST.UnsignedIntTy, NumElts: 3));
1826 AddStore("Store4", AST.getExtVectorType(VectorType: AST.UnsignedIntTy, NumElts: 4));
1827
1828 // Templated Store<T>(); see addByteAddressBufferLoadMethods() above.
1829 AddStore("Store", AST.DependentTy, /*TransposeArg=*/true);
1830
1831 return *this;
1832}
1833
1834BuiltinTypeDeclBuilder &
1835BuiltinTypeDeclBuilder::addByteAddressBufferInterlockedMethods() {
1836 assert(!Record->isCompleteDefinition() && "record is already complete");
1837 ASTContext &AST = SemaRef.getASTContext();
1838
1839 // This is a helper that declares two overloads with and without an out
1840 // original-value parameter for each entry, except where the original value
1841 // is required.
1842 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedAdd", ValueTy: AST.UnsignedIntTy,
1843 BuiltinName: "__builtin_hlsl_interlocked_add");
1844 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedAnd", ValueTy: AST.UnsignedIntTy,
1845 BuiltinName: "__builtin_hlsl_interlocked_and");
1846 addByteAddressBufferInterlockedCompareMethod(
1847 MethodName: "InterlockedCompareExchange", ValueTy: AST.UnsignedIntTy,
1848 BuiltinName: "__builtin_hlsl_interlocked_compare_exchange",
1849 /*WithOriginalValue=*/true);
1850 addByteAddressBufferInterlockedCompareMethod(
1851 MethodName: "InterlockedCompareExchangeFloatBitwise", ValueTy: AST.FloatTy,
1852 BuiltinName: "__builtin_hlsl_interlocked_compare_exchange_float_bitwise",
1853 /*WithOriginalValue=*/true);
1854 addByteAddressBufferInterlockedCompareMethod(
1855 MethodName: "InterlockedCompareStore", ValueTy: AST.UnsignedIntTy,
1856 BuiltinName: "__builtin_hlsl_interlocked_compare_store");
1857 addByteAddressBufferInterlockedCompareMethod(
1858 MethodName: "InterlockedCompareStoreFloatBitwise", ValueTy: AST.FloatTy,
1859 BuiltinName: "__builtin_hlsl_interlocked_compare_store_float_bitwise");
1860 addByteAddressBufferInterlockedMethod(
1861 MethodName: "InterlockedExchange", ValueTy: AST.UnsignedIntTy,
1862 BuiltinName: "__builtin_hlsl_interlocked_exchange", /*RequiresOriginalValue=*/true);
1863 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedExchangeFloat", ValueTy: AST.FloatTy,
1864 BuiltinName: "__builtin_hlsl_interlocked_exchange",
1865 /*RequiresOriginalValue=*/true);
1866 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedMax", ValueTy: AST.IntTy,
1867 BuiltinName: "__builtin_hlsl_interlocked_max");
1868 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedMax", ValueTy: AST.UnsignedIntTy,
1869 BuiltinName: "__builtin_hlsl_interlocked_max");
1870 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedMin", ValueTy: AST.IntTy,
1871 BuiltinName: "__builtin_hlsl_interlocked_min");
1872 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedMin", ValueTy: AST.UnsignedIntTy,
1873 BuiltinName: "__builtin_hlsl_interlocked_min");
1874 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedOr", ValueTy: AST.UnsignedIntTy,
1875 BuiltinName: "__builtin_hlsl_interlocked_or");
1876 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedXor", ValueTy: AST.UnsignedIntTy,
1877 BuiltinName: "__builtin_hlsl_interlocked_xor");
1878
1879 // Skip synthesizing the 64 bit methods on DXIL targets older than SM 6.6.
1880 const llvm::Triple &TT = AST.getTargetInfo().getTriple();
1881 bool HasInt64AtomicSupport =
1882 TT.getArch() != llvm::Triple::dxil ||
1883 AST.getTargetInfo().getPlatformMinVersion() >= VersionTuple(6, 6);
1884 if (HasInt64AtomicSupport) {
1885 // HLSL's uint64_t is `unsigned long`.
1886 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedAdd64",
1887 ValueTy: AST.UnsignedLongTy,
1888 BuiltinName: "__builtin_hlsl_interlocked_add");
1889 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedAnd64",
1890 ValueTy: AST.UnsignedLongTy,
1891 BuiltinName: "__builtin_hlsl_interlocked_and");
1892 addByteAddressBufferInterlockedCompareMethod(
1893 MethodName: "InterlockedCompareExchange64", ValueTy: AST.UnsignedLongTy,
1894 BuiltinName: "__builtin_hlsl_interlocked_compare_exchange",
1895 /*WithOriginalValue=*/true);
1896 addByteAddressBufferInterlockedCompareMethod(
1897 MethodName: "InterlockedCompareStore64", ValueTy: AST.UnsignedLongTy,
1898 BuiltinName: "__builtin_hlsl_interlocked_compare_store");
1899 addByteAddressBufferInterlockedMethod(
1900 MethodName: "InterlockedExchange64", ValueTy: AST.UnsignedLongTy,
1901 BuiltinName: "__builtin_hlsl_interlocked_exchange", /*RequiresOriginalValue=*/true);
1902 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedMax64", ValueTy: AST.LongTy,
1903 BuiltinName: "__builtin_hlsl_interlocked_max");
1904 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedMax64",
1905 ValueTy: AST.UnsignedLongTy,
1906 BuiltinName: "__builtin_hlsl_interlocked_max");
1907 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedMin64", ValueTy: AST.LongTy,
1908 BuiltinName: "__builtin_hlsl_interlocked_min");
1909 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedMin64",
1910 ValueTy: AST.UnsignedLongTy,
1911 BuiltinName: "__builtin_hlsl_interlocked_min");
1912 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedOr64", ValueTy: AST.UnsignedLongTy,
1913 BuiltinName: "__builtin_hlsl_interlocked_or");
1914 addByteAddressBufferInterlockedMethod(MethodName: "InterlockedXor64",
1915 ValueTy: AST.UnsignedLongTy,
1916 BuiltinName: "__builtin_hlsl_interlocked_xor");
1917 }
1918
1919 return *this;
1920}
1921
1922BuiltinTypeDeclBuilder &
1923BuiltinTypeDeclBuilder::addDerivativeAvailability(StringRef MethodName) {
1924 ASTContext &AST = Record->getASTContext();
1925 DeclarationName Name(&AST.Idents.get(Name: MethodName, TokenCode: tok::TokenKind::identifier));
1926 for (NamedDecl *D : Record->lookup(Name)) {
1927 if (auto *FTD = dyn_cast<FunctionTemplateDecl>(Val: D))
1928 D = FTD->getTemplatedDecl();
1929 if (auto *MD = dyn_cast<CXXMethodDecl>(Val: D))
1930 addDerivativeAvailabilityAttrs(AST, FD: MD);
1931 }
1932 return *this;
1933}
1934
1935BuiltinTypeDeclBuilder &
1936BuiltinTypeDeclBuilder::addSampleMethods(ResourceDimension Dim, bool IsArray) {
1937 assert(!Record->isCompleteDefinition() && "record is already complete");
1938 ASTContext &AST = Record->getASTContext();
1939 QualType ReturnType = getHandleElementType();
1940 QualType SamplerStateType =
1941 lookupBuiltinType(S&: SemaRef, Name: "SamplerState", DC: Record->getDeclContext());
1942 uint32_t OffsetSize = getResourceDimensions(Dim);
1943 uint32_t CoordSize = OffsetSize + (IsArray ? 1 : 0);
1944 QualType FloatTy = AST.FloatTy;
1945 QualType CoordTy = getVectorOrScalarType(AST, Ty: FloatTy, NumElements: CoordSize);
1946 QualType IntTy = AST.IntTy;
1947 QualType OffsetTy = getVectorOrScalarType(AST, Ty: IntTy, NumElements: OffsetSize);
1948 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1949
1950 // T Sample(SamplerState s, float2 location)
1951 BuiltinTypeMethodBuilder(*this, "Sample", ReturnType)
1952 .addParam(Name: "Sampler", Ty: SamplerStateType)
1953 .addParam(Name: "Location", Ty: CoordTy)
1954 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
1955 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample", ReturnType, ArgSpecs: PH::Handle,
1956 ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1)
1957 .returnValue(ReturnValue: PH::LastStmt)
1958 .finalize();
1959
1960 // Resources without offsets have a clamp overload that takes no offset.
1961 if (!hasResourceOffset(Dim)) {
1962 // T Sample(SamplerState s, float3 location, float clamp)
1963 BuiltinTypeMethodBuilder(*this, "Sample", ReturnType)
1964 .addParam(Name: "Sampler", Ty: SamplerStateType)
1965 .addParam(Name: "Location", Ty: CoordTy)
1966 .addParam(Name: "Clamp", Ty: FloatTy)
1967 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
1968 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample", ReturnType, ArgSpecs: PH::Handle,
1969 ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2)
1970 .returnValue(ReturnValue: PH::LastStmt)
1971 .finalize();
1972
1973 // Sample uses implicit derivatives to calculate the mip level.
1974 return addDerivativeAvailability(MethodName: "Sample");
1975 }
1976
1977 // T Sample(SamplerState s, float2 location, int2 offset)
1978 BuiltinTypeMethodBuilder(*this, "Sample", ReturnType)
1979 .addParam(Name: "Sampler", Ty: SamplerStateType)
1980 .addParam(Name: "Location", Ty: CoordTy)
1981 .addParam(Name: "Offset", Ty: OffsetTy)
1982 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
1983 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample", ReturnType, ArgSpecs: PH::Handle,
1984 ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2)
1985 .returnValue(ReturnValue: PH::LastStmt)
1986 .finalize();
1987
1988 // T Sample(SamplerState s, float2 location, int2 offset, float clamp)
1989 BuiltinTypeMethodBuilder(*this, "Sample", ReturnType)
1990 .addParam(Name: "Sampler", Ty: SamplerStateType)
1991 .addParam(Name: "Location", Ty: CoordTy)
1992 .addParam(Name: "Offset", Ty: OffsetTy)
1993 .addParam(Name: "Clamp", Ty: FloatTy)
1994 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
1995 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample", ReturnType, ArgSpecs: PH::Handle,
1996 ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3)
1997 .returnValue(ReturnValue: PH::LastStmt)
1998 .finalize();
1999
2000 // Sample uses implicit derivatives to calculate the mip level.
2001 return addDerivativeAvailability(MethodName: "Sample");
2002}
2003
2004BuiltinTypeDeclBuilder &
2005BuiltinTypeDeclBuilder::addSampleBiasMethods(ResourceDimension Dim,
2006 bool IsArray) {
2007 assert(!Record->isCompleteDefinition() && "record is already complete");
2008 ASTContext &AST = Record->getASTContext();
2009 QualType ReturnType = getHandleElementType();
2010 QualType SamplerStateType =
2011 lookupBuiltinType(S&: SemaRef, Name: "SamplerState", DC: Record->getDeclContext());
2012 uint32_t OffsetSize = getResourceDimensions(Dim);
2013 uint32_t CoordSize = OffsetSize + (IsArray ? 1 : 0);
2014 QualType FloatTy = AST.FloatTy;
2015 QualType CoordTy = getVectorOrScalarType(AST, Ty: FloatTy, NumElements: CoordSize);
2016 QualType IntTy = AST.IntTy;
2017 QualType OffsetTy = getVectorOrScalarType(AST, Ty: IntTy, NumElements: OffsetSize);
2018 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2019
2020 // T SampleBias(SamplerState s, float2 location, float bias)
2021 BuiltinTypeMethodBuilder(*this, "SampleBias", ReturnType)
2022 .addParam(Name: "Sampler", Ty: SamplerStateType)
2023 .addParam(Name: "Location", Ty: CoordTy)
2024 .addParam(Name: "Bias", Ty: FloatTy)
2025 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2026 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample_bias", ReturnType,
2027 ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2)
2028 .returnValue(ReturnValue: PH::LastStmt)
2029 .finalize();
2030
2031 // Resources without offsets have a clamp overload that takes no offset.
2032 if (!hasResourceOffset(Dim)) {
2033 // T SampleBias(SamplerState s, float3 location, float bias, float clamp)
2034 BuiltinTypeMethodBuilder(*this, "SampleBias", ReturnType)
2035 .addParam(Name: "Sampler", Ty: SamplerStateType)
2036 .addParam(Name: "Location", Ty: CoordTy)
2037 .addParam(Name: "Bias", Ty: FloatTy)
2038 .addParam(Name: "Clamp", Ty: FloatTy)
2039 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2040 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample_bias", ReturnType,
2041 ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3)
2042 .returnValue(ReturnValue: PH::LastStmt)
2043 .finalize();
2044
2045 // SampleBias uses implicit derivatives to calculate the mip level.
2046 return addDerivativeAvailability(MethodName: "SampleBias");
2047 }
2048
2049 // T SampleBias(SamplerState s, float2 location, float bias, int2 offset)
2050 BuiltinTypeMethodBuilder(*this, "SampleBias", ReturnType)
2051 .addParam(Name: "Sampler", Ty: SamplerStateType)
2052 .addParam(Name: "Location", Ty: CoordTy)
2053 .addParam(Name: "Bias", Ty: FloatTy)
2054 .addParam(Name: "Offset", Ty: OffsetTy)
2055 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2056 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample_bias", ReturnType,
2057 ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3)
2058 .returnValue(ReturnValue: PH::LastStmt)
2059 .finalize();
2060
2061 // T SampleBias(SamplerState s, float2 location, float bias, int2 offset,
2062 // float clamp)
2063 BuiltinTypeMethodBuilder(*this, "SampleBias", ReturnType)
2064 .addParam(Name: "Sampler", Ty: SamplerStateType)
2065 .addParam(Name: "Location", Ty: CoordTy)
2066 .addParam(Name: "Bias", Ty: FloatTy)
2067 .addParam(Name: "Offset", Ty: OffsetTy)
2068 .addParam(Name: "Clamp", Ty: FloatTy)
2069 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2070 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample_bias", ReturnType,
2071 ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3, ArgSpecs: PH::_4)
2072 .returnValue(ReturnValue: PH::LastStmt)
2073 .finalize();
2074
2075 // SampleBias uses implicit derivatives to calculate the mip level.
2076 return addDerivativeAvailability(MethodName: "SampleBias");
2077}
2078
2079BuiltinTypeDeclBuilder &
2080BuiltinTypeDeclBuilder::addSampleGradMethods(ResourceDimension Dim,
2081 bool IsArray) {
2082 assert(!Record->isCompleteDefinition() && "record is already complete");
2083 ASTContext &AST = Record->getASTContext();
2084 QualType ReturnType = getHandleElementType();
2085 QualType SamplerStateType =
2086 lookupBuiltinType(S&: SemaRef, Name: "SamplerState", DC: Record->getDeclContext());
2087 uint32_t OffsetSize = getResourceDimensions(Dim);
2088 uint32_t CoordSize = OffsetSize + (IsArray ? 1 : 0);
2089 QualType FloatTy = AST.FloatTy;
2090 QualType CoordTy = getVectorOrScalarType(AST, Ty: FloatTy, NumElements: CoordSize);
2091 QualType OffsetFloatTy = getVectorOrScalarType(AST, Ty: FloatTy, NumElements: OffsetSize);
2092 QualType IntTy = AST.IntTy;
2093 QualType OffsetTy = getVectorOrScalarType(AST, Ty: IntTy, NumElements: OffsetSize);
2094 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2095
2096 // T SampleGrad(SamplerState s, float2 location, float2 ddx, float2 ddy)
2097 BuiltinTypeMethodBuilder(*this, "SampleGrad", ReturnType)
2098 .addParam(Name: "Sampler", Ty: SamplerStateType)
2099 .addParam(Name: "Location", Ty: CoordTy)
2100 .addParam(Name: "DDX", Ty: OffsetFloatTy)
2101 .addParam(Name: "DDY", Ty: OffsetFloatTy)
2102 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2103 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample_grad", ReturnType,
2104 ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3)
2105 .returnValue(ReturnValue: PH::LastStmt)
2106 .finalize();
2107
2108 // Resources without offsets have a clamp overload that takes no offset.
2109 if (!hasResourceOffset(Dim)) {
2110 // T SampleGrad(SamplerState s, float3 location, float3 ddx, float3 ddy,
2111 // float clamp)
2112 BuiltinTypeMethodBuilder(*this, "SampleGrad", ReturnType)
2113 .addParam(Name: "Sampler", Ty: SamplerStateType)
2114 .addParam(Name: "Location", Ty: CoordTy)
2115 .addParam(Name: "DDX", Ty: OffsetFloatTy)
2116 .addParam(Name: "DDY", Ty: OffsetFloatTy)
2117 .addParam(Name: "Clamp", Ty: FloatTy)
2118 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2119 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample_grad", ReturnType,
2120 ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3, ArgSpecs: PH::_4)
2121 .returnValue(ReturnValue: PH::LastStmt)
2122 .finalize();
2123 return *this;
2124 }
2125
2126 // T SampleGrad(SamplerState s, float2 location, float2 ddx, float2 ddy,
2127 // int2 offset)
2128 BuiltinTypeMethodBuilder(*this, "SampleGrad", ReturnType)
2129 .addParam(Name: "Sampler", Ty: SamplerStateType)
2130 .addParam(Name: "Location", Ty: CoordTy)
2131 .addParam(Name: "DDX", Ty: OffsetFloatTy)
2132 .addParam(Name: "DDY", Ty: OffsetFloatTy)
2133 .addParam(Name: "Offset", Ty: OffsetTy)
2134 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2135 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample_grad", ReturnType,
2136 ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3, ArgSpecs: PH::_4)
2137 .returnValue(ReturnValue: PH::LastStmt)
2138 .finalize();
2139
2140 // T SampleGrad(SamplerState s, float2 location, float2 ddx, float2 ddy,
2141 // int2 offset, float clamp)
2142 BuiltinTypeMethodBuilder(*this, "SampleGrad", ReturnType)
2143 .addParam(Name: "Sampler", Ty: SamplerStateType)
2144 .addParam(Name: "Location", Ty: CoordTy)
2145 .addParam(Name: "DDX", Ty: OffsetFloatTy)
2146 .addParam(Name: "DDY", Ty: OffsetFloatTy)
2147 .addParam(Name: "Offset", Ty: OffsetTy)
2148 .addParam(Name: "Clamp", Ty: FloatTy)
2149 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2150 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample_grad", ReturnType,
2151 ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3, ArgSpecs: PH::_4,
2152 ArgSpecs: PH::_5)
2153 .returnValue(ReturnValue: PH::LastStmt)
2154 .finalize();
2155
2156 return *this;
2157}
2158
2159BuiltinTypeDeclBuilder &
2160BuiltinTypeDeclBuilder::addSampleLevelMethods(ResourceDimension Dim,
2161 bool IsArray) {
2162 assert(!Record->isCompleteDefinition() && "record is already complete");
2163 ASTContext &AST = Record->getASTContext();
2164 QualType ReturnType = getHandleElementType();
2165 QualType SamplerStateType =
2166 lookupBuiltinType(S&: SemaRef, Name: "SamplerState", DC: Record->getDeclContext());
2167 uint32_t OffsetSize = getResourceDimensions(Dim);
2168 uint32_t CoordSize = OffsetSize + (IsArray ? 1 : 0);
2169 QualType FloatTy = AST.FloatTy;
2170 QualType CoordTy = getVectorOrScalarType(AST, Ty: FloatTy, NumElements: CoordSize);
2171 QualType IntTy = AST.IntTy;
2172 QualType OffsetTy = getVectorOrScalarType(AST, Ty: IntTy, NumElements: OffsetSize);
2173 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2174
2175 // T SampleLevel(SamplerState s, float2 location, float lod)
2176 BuiltinTypeMethodBuilder(*this, "SampleLevel", ReturnType)
2177 .addParam(Name: "Sampler", Ty: SamplerStateType)
2178 .addParam(Name: "Location", Ty: CoordTy)
2179 .addParam(Name: "LOD", Ty: FloatTy)
2180 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2181 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample_level", ReturnType,
2182 ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2)
2183 .returnValue(ReturnValue: PH::LastStmt)
2184 .finalize();
2185
2186 // Resources without offsets have no offset overloads.
2187 if (!hasResourceOffset(Dim))
2188 return *this;
2189
2190 // T SampleLevel(SamplerState s, float2 location, float lod, int2 offset)
2191 BuiltinTypeMethodBuilder(*this, "SampleLevel", ReturnType)
2192 .addParam(Name: "Sampler", Ty: SamplerStateType)
2193 .addParam(Name: "Location", Ty: CoordTy)
2194 .addParam(Name: "LOD", Ty: FloatTy)
2195 .addParam(Name: "Offset", Ty: OffsetTy)
2196 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2197 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample_level", ReturnType,
2198 ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3)
2199 .returnValue(ReturnValue: PH::LastStmt)
2200 .finalize();
2201
2202 return *this;
2203}
2204
2205BuiltinTypeDeclBuilder &
2206BuiltinTypeDeclBuilder::addSampleCmpMethods(ResourceDimension Dim,
2207 bool IsArray) {
2208 assert(!Record->isCompleteDefinition() && "record is already complete");
2209 ASTContext &AST = Record->getASTContext();
2210 QualType ReturnType = AST.FloatTy;
2211 QualType SamplerComparisonStateType = lookupBuiltinType(
2212 S&: SemaRef, Name: "SamplerComparisonState", DC: Record->getDeclContext());
2213 uint32_t OffsetSize = getResourceDimensions(Dim);
2214 uint32_t CoordSize = OffsetSize + (IsArray ? 1 : 0);
2215 QualType FloatTy = AST.FloatTy;
2216 QualType CoordTy = getVectorOrScalarType(AST, Ty: FloatTy, NumElements: CoordSize);
2217 QualType IntTy = AST.IntTy;
2218 QualType OffsetTy = getVectorOrScalarType(AST, Ty: IntTy, NumElements: OffsetSize);
2219 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2220
2221 // T SampleCmp(SamplerComparisonState s, float2 location, float compare_value)
2222 BuiltinTypeMethodBuilder(*this, "SampleCmp", ReturnType)
2223 .addParam(Name: "Sampler", Ty: SamplerComparisonStateType)
2224 .addParam(Name: "Location", Ty: CoordTy)
2225 .addParam(Name: "CompareValue", Ty: FloatTy)
2226 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2227 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample_cmp", ReturnType, ArgSpecs: PH::Handle,
2228 ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2)
2229 .returnValue(ReturnValue: PH::LastStmt)
2230 .finalize();
2231
2232 // Resources without offsets have a clamp overload that takes no offset.
2233 if (!hasResourceOffset(Dim)) {
2234 // T SampleCmp(SamplerComparisonState s, float3 location, float
2235 // compare_value, float clamp)
2236 BuiltinTypeMethodBuilder(*this, "SampleCmp", ReturnType)
2237 .addParam(Name: "Sampler", Ty: SamplerComparisonStateType)
2238 .addParam(Name: "Location", Ty: CoordTy)
2239 .addParam(Name: "CompareValue", Ty: FloatTy)
2240 .addParam(Name: "Clamp", Ty: FloatTy)
2241 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2242 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample_cmp", ReturnType,
2243 ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3)
2244 .returnValue(ReturnValue: PH::LastStmt)
2245 .finalize();
2246
2247 // SampleCmp uses implicit derivatives to calculate the mip level.
2248 return addDerivativeAvailability(MethodName: "SampleCmp");
2249 }
2250
2251 // T SampleCmp(SamplerComparisonState s, float2 location, float
2252 // compare_value, int2 offset)
2253 BuiltinTypeMethodBuilder(*this, "SampleCmp", ReturnType)
2254 .addParam(Name: "Sampler", Ty: SamplerComparisonStateType)
2255 .addParam(Name: "Location", Ty: CoordTy)
2256 .addParam(Name: "CompareValue", Ty: FloatTy)
2257 .addParam(Name: "Offset", Ty: OffsetTy)
2258 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2259 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample_cmp", ReturnType, ArgSpecs: PH::Handle,
2260 ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3)
2261 .returnValue(ReturnValue: PH::LastStmt)
2262 .finalize();
2263
2264 // T SampleCmp(SamplerComparisonState s, float2 location, float
2265 // compare_value, int2 offset, float clamp)
2266 BuiltinTypeMethodBuilder(*this, "SampleCmp", ReturnType)
2267 .addParam(Name: "Sampler", Ty: SamplerComparisonStateType)
2268 .addParam(Name: "Location", Ty: CoordTy)
2269 .addParam(Name: "CompareValue", Ty: FloatTy)
2270 .addParam(Name: "Offset", Ty: OffsetTy)
2271 .addParam(Name: "Clamp", Ty: FloatTy)
2272 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2273 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample_cmp", ReturnType, ArgSpecs: PH::Handle,
2274 ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3, ArgSpecs: PH::_4)
2275 .returnValue(ReturnValue: PH::LastStmt)
2276 .finalize();
2277
2278 // SampleCmp uses implicit derivatives to calculate the mip level.
2279 return addDerivativeAvailability(MethodName: "SampleCmp");
2280}
2281
2282BuiltinTypeDeclBuilder &
2283BuiltinTypeDeclBuilder::addSampleCmpLevelZeroMethods(ResourceDimension Dim,
2284 bool IsArray) {
2285 assert(!Record->isCompleteDefinition() && "record is already complete");
2286 ASTContext &AST = Record->getASTContext();
2287 QualType ReturnType = AST.FloatTy;
2288 QualType SamplerComparisonStateType = lookupBuiltinType(
2289 S&: SemaRef, Name: "SamplerComparisonState", DC: Record->getDeclContext());
2290 uint32_t OffsetSize = getResourceDimensions(Dim);
2291 uint32_t CoordSize = OffsetSize + (IsArray ? 1 : 0);
2292 QualType FloatTy = AST.FloatTy;
2293 QualType CoordTy = getVectorOrScalarType(AST, Ty: FloatTy, NumElements: CoordSize);
2294 QualType IntTy = AST.IntTy;
2295 QualType OffsetTy = getVectorOrScalarType(AST, Ty: IntTy, NumElements: OffsetSize);
2296 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2297
2298 // T SampleCmpLevelZero(SamplerComparisonState s, float2 location, float
2299 // compare_value)
2300 BuiltinTypeMethodBuilder(*this, "SampleCmpLevelZero", ReturnType)
2301 .addParam(Name: "Sampler", Ty: SamplerComparisonStateType)
2302 .addParam(Name: "Location", Ty: CoordTy)
2303 .addParam(Name: "CompareValue", Ty: FloatTy)
2304 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2305 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample_cmp_level_zero", ReturnType,
2306 ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2)
2307 .returnValue(ReturnValue: PH::LastStmt)
2308 .finalize();
2309
2310 // Resources without offsets have no offset overloads.
2311 if (!hasResourceOffset(Dim))
2312 return *this;
2313
2314 // T SampleCmpLevelZero(SamplerComparisonState s, float2 location, float
2315 // compare_value, int2 offset)
2316 BuiltinTypeMethodBuilder(*this, "SampleCmpLevelZero", ReturnType)
2317 .addParam(Name: "Sampler", Ty: SamplerComparisonStateType)
2318 .addParam(Name: "Location", Ty: CoordTy)
2319 .addParam(Name: "CompareValue", Ty: FloatTy)
2320 .addParam(Name: "Offset", Ty: OffsetTy)
2321 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2322 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample_cmp_level_zero", ReturnType,
2323 ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3)
2324 .returnValue(ReturnValue: PH::LastStmt)
2325 .finalize();
2326
2327 return *this;
2328}
2329
2330BuiltinTypeDeclBuilder &
2331BuiltinTypeDeclBuilder::addGetDimensionsMethods(ResourceDimension Dim) {
2332 assert(!Record->isCompleteDefinition() && "record is already complete");
2333 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2334 ASTContext &AST = SemaRef.getASTContext();
2335 QualType UIntTy = AST.UnsignedIntTy;
2336
2337 assert(Dim != ResourceDimension::Unknown);
2338
2339 QualType FloatTy = AST.FloatTy;
2340 // Add overloads for uint and float.
2341 QualType Params[] = {UIntTy, FloatTy};
2342
2343 for (QualType OutTy : Params) {
2344 if (Dim == ResourceDimension::Dim2D) {
2345 StringRef XYName = "__builtin_hlsl_resource_getdimensions_xy";
2346 StringRef LevelsXYName =
2347 "__builtin_hlsl_resource_getdimensions_levels_xy";
2348
2349 if (OutTy == FloatTy) {
2350 XYName = "__builtin_hlsl_resource_getdimensions_xy_float";
2351 LevelsXYName = "__builtin_hlsl_resource_getdimensions_levels_xy_float";
2352 }
2353
2354 // void GetDimensions(out [uint|float] width, out [uint|float] height)
2355 BuiltinTypeMethodBuilder(*this, "GetDimensions", AST.VoidTy)
2356 .addParam(Name: "width", Ty: OutTy, Modifier: HLSLParamModifierAttr::Keyword_out)
2357 .addParam(Name: "height", Ty: OutTy, Modifier: HLSLParamModifierAttr::Keyword_out)
2358 .callBuiltin(BuiltinName: XYName, ReturnType: QualType(), ArgSpecs: PH::Handle, ArgSpecs: PH::_0, ArgSpecs: PH::_1)
2359 .finalize();
2360
2361 // void GetDimensions(uint mipLevel, out [uint|float] width, out
2362 // [uint|float] height, out [uint|float] numberOfLevels)
2363 BuiltinTypeMethodBuilder(*this, "GetDimensions", AST.VoidTy)
2364 .addParam(Name: "mipLevel", Ty: UIntTy)
2365 .addParam(Name: "width", Ty: OutTy, Modifier: HLSLParamModifierAttr::Keyword_out)
2366 .addParam(Name: "height", Ty: OutTy, Modifier: HLSLParamModifierAttr::Keyword_out)
2367 .addParam(Name: "numberOfLevels", Ty: OutTy, Modifier: HLSLParamModifierAttr::Keyword_out)
2368 .callBuiltin(BuiltinName: LevelsXYName, ReturnType: QualType(), ArgSpecs: PH::Handle, ArgSpecs: PH::_0, ArgSpecs: PH::_1,
2369 ArgSpecs: PH::_2, ArgSpecs: PH::_3)
2370 .finalize();
2371 }
2372 }
2373
2374 return *this;
2375}
2376
2377BuiltinTypeDeclBuilder &
2378BuiltinTypeDeclBuilder::addCalculateLodMethods(ResourceDimension Dim) {
2379 assert(!Record->isCompleteDefinition() && "record is already complete");
2380 ASTContext &AST = Record->getASTContext();
2381 QualType ReturnType = AST.FloatTy;
2382 QualType SamplerStateType =
2383 lookupBuiltinType(S&: SemaRef, Name: "SamplerState", DC: Record->getDeclContext());
2384 uint32_t VecSize = getResourceDimensions(Dim);
2385 QualType FloatTy = AST.FloatTy;
2386 QualType LocationTy = getVectorOrScalarType(AST, Ty: FloatTy, NumElements: VecSize);
2387 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2388
2389 // float CalculateLevelOfDetail(SamplerState s, float2 location)
2390 BuiltinTypeMethodBuilder(*this, "CalculateLevelOfDetail", ReturnType)
2391 .addParam(Name: "Sampler", Ty: SamplerStateType)
2392 .addParam(Name: "Location", Ty: LocationTy)
2393 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2394 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_calculate_lod", ReturnType,
2395 ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1)
2396 .finalize();
2397
2398 // float CalculateLevelOfDetailUnclamped(SamplerState s, float2 location)
2399 BuiltinTypeMethodBuilder(*this, "CalculateLevelOfDetailUnclamped", ReturnType)
2400 .addParam(Name: "Sampler", Ty: SamplerStateType)
2401 .addParam(Name: "Location", Ty: LocationTy)
2402 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2403 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_calculate_lod_unclamped",
2404 ReturnType, ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1)
2405 .finalize();
2406
2407 // Both methods use implicit derivatives to calculate the level of detail.
2408 addDerivativeAvailability(MethodName: "CalculateLevelOfDetail");
2409 return addDerivativeAvailability(MethodName: "CalculateLevelOfDetailUnclamped");
2410}
2411
2412QualType BuiltinTypeDeclBuilder::getGatherReturnType() {
2413 ASTContext &AST = SemaRef.getASTContext();
2414 QualType T = getHandleElementType();
2415 if (T.isNull())
2416 return QualType();
2417
2418 if (const auto *VT = T->getAs<VectorType>())
2419 T = VT->getElementType();
2420 else if (const auto *DT = T->getAs<DependentSizedExtVectorType>())
2421 T = DT->getElementType();
2422
2423 return AST.getExtVectorType(VectorType: T, NumElts: 4);
2424}
2425
2426BuiltinTypeDeclBuilder &
2427BuiltinTypeDeclBuilder::addGatherMethods(ResourceDimension Dim, bool IsArray) {
2428 assert(!Record->isCompleteDefinition() && "record is already complete");
2429 ASTContext &AST = Record->getASTContext();
2430 QualType ReturnType = getGatherReturnType();
2431
2432 QualType SamplerStateType =
2433 lookupBuiltinType(S&: SemaRef, Name: "SamplerState", DC: Record->getDeclContext());
2434 uint32_t OffsetSize = getResourceDimensions(Dim);
2435 uint32_t CoordSize = OffsetSize + (IsArray ? 1 : 0);
2436 QualType LocationTy = AST.FloatTy;
2437 QualType CoordTy = getVectorOrScalarType(AST, Ty: LocationTy, NumElements: CoordSize);
2438 QualType IntTy = AST.IntTy;
2439 QualType OffsetTy = getVectorOrScalarType(AST, Ty: IntTy, NumElements: OffsetSize);
2440 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2441
2442 // Overloads for Gather, GatherRed, GatherGreen, GatherBlue, GatherAlpha
2443 struct GatherVariant {
2444 const char *Name;
2445 int Component;
2446 };
2447 GatherVariant Variants[] = {{.Name: "Gather", .Component: 0},
2448 {.Name: "GatherRed", .Component: 0},
2449 {.Name: "GatherGreen", .Component: 1},
2450 {.Name: "GatherBlue", .Component: 2},
2451 {.Name: "GatherAlpha", .Component: 3}};
2452
2453 for (const auto &V : Variants) {
2454 // ret GatherVariant(SamplerState s, float2 location)
2455 BuiltinTypeMethodBuilder(*this, V.Name, ReturnType)
2456 .addParam(Name: "Sampler", Ty: SamplerStateType)
2457 .addParam(Name: "Location", Ty: CoordTy)
2458 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2459 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_gather", ReturnType, ArgSpecs: PH::Handle,
2460 ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1,
2461 ArgSpecs: getConstantUnsignedIntExpr(value: V.Component))
2462 .finalize();
2463
2464 // Resources without offsets have no offset overloads.
2465 if (!hasResourceOffset(Dim))
2466 continue;
2467
2468 // ret GatherVariant(SamplerState s, float2 location, int2 offset)
2469 BuiltinTypeMethodBuilder(*this, V.Name, ReturnType)
2470 .addParam(Name: "Sampler", Ty: SamplerStateType)
2471 .addParam(Name: "Location", Ty: CoordTy)
2472 .addParam(Name: "Offset", Ty: OffsetTy)
2473 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2474 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_gather", ReturnType, ArgSpecs: PH::Handle,
2475 ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1,
2476 ArgSpecs: getConstantUnsignedIntExpr(value: V.Component), ArgSpecs: PH::_2)
2477 .finalize();
2478 }
2479
2480 return *this;
2481}
2482
2483BuiltinTypeDeclBuilder &
2484BuiltinTypeDeclBuilder::addGatherCmpMethods(ResourceDimension Dim,
2485 bool IsArray) {
2486 assert(!Record->isCompleteDefinition() && "record is already complete");
2487 ASTContext &AST = Record->getASTContext();
2488 QualType ReturnType = AST.getExtVectorType(VectorType: AST.FloatTy, NumElts: 4);
2489
2490 QualType SamplerComparisonStateType = lookupBuiltinType(
2491 S&: SemaRef, Name: "SamplerComparisonState", DC: Record->getDeclContext());
2492 uint32_t OffsetSize = getResourceDimensions(Dim);
2493 uint32_t CoordSize = OffsetSize + (IsArray ? 1 : 0);
2494 QualType FloatTy = AST.FloatTy;
2495 QualType CoordTy = getVectorOrScalarType(AST, Ty: FloatTy, NumElements: CoordSize);
2496 QualType IntTy = AST.IntTy;
2497 QualType OffsetTy = getVectorOrScalarType(AST, Ty: IntTy, NumElements: OffsetSize);
2498 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2499
2500 // Overloads for GatherCmp, GatherCmpRed, GatherCmpGreen, GatherCmpBlue,
2501 // GatherCmpAlpha
2502 struct GatherVariant {
2503 const char *Name;
2504 int Component;
2505 };
2506 GatherVariant Variants[] = {{.Name: "GatherCmp", .Component: 0},
2507 {.Name: "GatherCmpRed", .Component: 0},
2508 {.Name: "GatherCmpGreen", .Component: 1},
2509 {.Name: "GatherCmpBlue", .Component: 2},
2510 {.Name: "GatherCmpAlpha", .Component: 3}};
2511
2512 for (const auto &V : Variants) {
2513 // ret GatherCmpVariant(SamplerComparisonState s, float2 location, float
2514 // compare_value)
2515 BuiltinTypeMethodBuilder(*this, V.Name, ReturnType)
2516 .addParam(Name: "Sampler", Ty: SamplerComparisonStateType)
2517 .addParam(Name: "Location", Ty: CoordTy)
2518 .addParam(Name: "CompareValue", Ty: FloatTy)
2519 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2520 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_gather_cmp", ReturnType,
2521 ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2,
2522 ArgSpecs: getConstantUnsignedIntExpr(value: V.Component))
2523 .finalize();
2524
2525 // Resources without offsets have no offset overloads.
2526 if (!hasResourceOffset(Dim))
2527 continue;
2528
2529 // ret GatherCmpVariant(SamplerComparisonState s, float2 location, float
2530 // compare_value, int2 offset)
2531 BuiltinTypeMethodBuilder(*this, V.Name, ReturnType)
2532 .addParam(Name: "Sampler", Ty: SamplerComparisonStateType)
2533 .addParam(Name: "Location", Ty: CoordTy)
2534 .addParam(Name: "CompareValue", Ty: FloatTy)
2535 .addParam(Name: "Offset", Ty: OffsetTy)
2536 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
2537 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_gather_cmp", ReturnType,
2538 ArgSpecs: PH::Handle, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2,
2539 ArgSpecs: getConstantUnsignedIntExpr(value: V.Component), ArgSpecs: PH::_3)
2540 .finalize();
2541 }
2542
2543 return *this;
2544}
2545
2546FieldDecl *BuiltinTypeDeclBuilder::getResourceHandleField() const {
2547 auto I = Fields.find(Key: "__handle");
2548 assert(I != Fields.end() &&
2549 I->second->getType()->isHLSLAttributedResourceType() &&
2550 "record does not have resource handle field");
2551 return I->second;
2552}
2553
2554FieldDecl *BuiltinTypeDeclBuilder::getResourceCounterHandleField() const {
2555 auto I = Fields.find(Key: "__counter_handle");
2556 if (I == Fields.end() ||
2557 !I->second->getType()->isHLSLAttributedResourceType())
2558 return nullptr;
2559 return I->second;
2560}
2561
2562QualType BuiltinTypeDeclBuilder::getFirstTemplateTypeParam() {
2563 assert(Template && "record it not a template");
2564 if (const auto *TTD = dyn_cast<TemplateTypeParmDecl>(
2565 Val: Template->getTemplateParameters()->getParam(Idx: 0))) {
2566 return QualType(TTD->getTypeForDecl(), 0);
2567 }
2568 return QualType();
2569}
2570
2571QualType BuiltinTypeDeclBuilder::getHandleElementType() {
2572 if (Template)
2573 return getFirstTemplateTypeParam();
2574
2575 if (auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(Val: Record)) {
2576 const auto &Args = Spec->getTemplateArgs();
2577 if (Args.size() > 0 && Args[0].getKind() == TemplateArgument::Type)
2578 return Args[0].getAsType();
2579 }
2580
2581 // TODO: Should we default to VoidTy? Using `i8` is arguably ambiguous.
2582 return SemaRef.getASTContext().Char8Ty;
2583}
2584
2585HLSLAttributedResourceType::Attributes
2586BuiltinTypeDeclBuilder::getResourceAttrs() const {
2587 QualType HandleType = getResourceHandleField()->getType();
2588 return cast<HLSLAttributedResourceType>(Val&: HandleType)->getAttrs();
2589}
2590
2591BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::completeDefinition() {
2592 assert(!Record->isCompleteDefinition() && "record is already complete");
2593 assert(Record->isBeingDefined() &&
2594 "Definition must be started before completing it.");
2595
2596 Record->completeDefinition();
2597 Record->setIsHLSLBuiltinRecord(true);
2598 return *this;
2599}
2600
2601Expr *BuiltinTypeDeclBuilder::getConstantIntExpr(int value) {
2602 ASTContext &AST = SemaRef.getASTContext();
2603 return IntegerLiteral::Create(
2604 C: AST, V: llvm::APInt(AST.getTypeSize(T: AST.IntTy), value, true), type: AST.IntTy,
2605 l: SourceLocation());
2606}
2607
2608Expr *BuiltinTypeDeclBuilder::getConstantUnsignedIntExpr(unsigned value) {
2609 ASTContext &AST = SemaRef.getASTContext();
2610 return IntegerLiteral::Create(
2611 C: AST, V: llvm::APInt(AST.getTypeSize(T: AST.UnsignedIntTy), value),
2612 type: AST.UnsignedIntTy, l: SourceLocation());
2613}
2614
2615BuiltinTypeDeclBuilder &
2616BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
2617 ConceptDecl *CD) {
2618 return addSimpleTemplateParams(Names, DefaultTypes: {}, CD);
2619}
2620
2621BuiltinTypeDeclBuilder &
2622BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
2623 ArrayRef<QualType> DefaultTypes,
2624 ConceptDecl *CD) {
2625 if (Record->isCompleteDefinition()) {
2626 assert(Template && "existing record it not a template");
2627 assert(Template->getTemplateParameters()->size() == Names.size() &&
2628 "template param count mismatch");
2629 return *this;
2630 }
2631
2632 assert((DefaultTypes.empty() || DefaultTypes.size() == Names.size()) &&
2633 "template default argument count mismatch");
2634
2635 TemplateParameterListBuilder Builder = TemplateParameterListBuilder(*this);
2636 for (unsigned i = 0; i < Names.size(); ++i) {
2637 QualType DefaultTy = DefaultTypes.empty() ? QualType() : DefaultTypes[i];
2638 Builder.addTypeParameter(Name: Names[i], DefaultValue: DefaultTy);
2639 }
2640 return Builder.finalizeTemplateArgs(CD);
2641}
2642
2643BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addMSTextureTemplateParams(
2644 StringRef ElementName, StringRef SampleCountName, ConceptDecl *CD) {
2645 if (Record->isCompleteDefinition()) {
2646 assert(Template && "existing record it not a template");
2647 assert(Template->getTemplateParameters()->size() == 2 &&
2648 "template param count mismatch");
2649 return *this;
2650 }
2651
2652 ASTContext &AST = SemaRef.getASTContext();
2653 TemplateParameterListBuilder Builder = TemplateParameterListBuilder(*this);
2654 // No default element type (`Texture2DMS` and `Texture2DMS<>` are errors).
2655 // A sample count of 0 means the count comes from the bound resource rather
2656 // than denoting zero samples.
2657 Builder.addTypeParameter(Name: ElementName);
2658 Builder.addNonTypeParameter(Name: SampleCountName, Ty: AST.IntTy,
2659 DefaultValue: getConstantIntExpr(value: 0));
2660 return Builder.finalizeTemplateArgs(CD);
2661}
2662
2663BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() {
2664 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2665 QualType UnsignedIntTy = SemaRef.getASTContext().UnsignedIntTy;
2666 return BuiltinTypeMethodBuilder(*this, "IncrementCounter", UnsignedIntTy)
2667 .callBuiltin(BuiltinName: "__builtin_hlsl_buffer_update_counter", ReturnType: UnsignedIntTy,
2668 ArgSpecs: PH::CounterHandle, ArgSpecs: getConstantIntExpr(value: 1))
2669 .finalize();
2670}
2671
2672BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
2673 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2674 QualType UnsignedIntTy = SemaRef.getASTContext().UnsignedIntTy;
2675 return BuiltinTypeMethodBuilder(*this, "DecrementCounter", UnsignedIntTy)
2676 .callBuiltin(BuiltinName: "__builtin_hlsl_buffer_update_counter", ReturnType: UnsignedIntTy,
2677 ArgSpecs: PH::CounterHandle, ArgSpecs: getConstantIntExpr(value: -1))
2678 .finalize();
2679}
2680
2681BuiltinTypeDeclBuilder &
2682BuiltinTypeDeclBuilder::addLoadWithStatusFunction(DeclarationName &Name,
2683 QualType ReturnTy) {
2684 assert(!Record->isCompleteDefinition() && "record is already complete");
2685 ASTContext &AST = SemaRef.getASTContext();
2686 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2687 bool NeedsTypedBuiltin = !ReturnTy.isNull();
2688
2689 // The empty QualType is a placeholder. The actual return type is set below.
2690 // All load methods will be const.
2691 BuiltinTypeMethodBuilder MMB(*this, Name, QualType(), true);
2692
2693 if (!NeedsTypedBuiltin)
2694 ReturnTy = getHandleElementType();
2695 if (ReturnTy == AST.DependentTy)
2696 ReturnTy = MMB.addTemplateTypeParam(Name: "element_type");
2697 MMB.ReturnTy = ReturnTy;
2698
2699 MMB.addParam(Name: "Index", Ty: AST.UnsignedIntTy)
2700 .addParam(Name: "Status", Ty: AST.UnsignedIntTy,
2701 Modifier: HLSLParamModifierAttr::Keyword_out);
2702
2703 if (NeedsTypedBuiltin)
2704 MMB.callBuiltin(BuiltinName: "__builtin_hlsl_resource_load_with_status_typed", ReturnType: ReturnTy,
2705 ArgSpecs: PH::Handle, ArgSpecs: PH::_0, ArgSpecs: PH::_1, ArgSpecs&: ReturnTy);
2706 else
2707 MMB.callBuiltin(BuiltinName: "__builtin_hlsl_resource_load_with_status", ReturnType: ReturnTy,
2708 ArgSpecs: PH::Handle, ArgSpecs: PH::_0, ArgSpecs: PH::_1);
2709
2710 return MMB.finalize();
2711}
2712
2713BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleAccessFunction(
2714 DeclarationName &Name, bool IsConstReturn, bool IsRef, QualType IndexTy,
2715 QualType ElemTy, bool TransposeResult) {
2716 assert(!Record->isCompleteDefinition() && "record is already complete");
2717 ASTContext &AST = SemaRef.getASTContext();
2718 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2719 bool NeedsTypedBuiltin = !ElemTy.isNull();
2720
2721 // The empty QualType is a placeholder. The actual return type is set below.
2722 // All access methods are const; none of them rebind the resource handle.
2723 BuiltinTypeMethodBuilder MMB(*this, Name, QualType(), true);
2724
2725 if (!NeedsTypedBuiltin)
2726 ElemTy = getHandleElementType();
2727 if (ElemTy == AST.DependentTy)
2728 ElemTy = MMB.addTemplateTypeParam(Name: "element_type");
2729 QualType AddrSpaceElemTy =
2730 AST.getAddrSpaceQualType(T: ElemTy, AddressSpace: LangAS::hlsl_device);
2731 QualType ElemPtrTy = AST.getPointerType(T: AddrSpaceElemTy);
2732 QualType ReturnTy;
2733
2734 if (IsRef) {
2735 ReturnTy = AddrSpaceElemTy;
2736 if (IsConstReturn)
2737 ReturnTy.addConst();
2738 ReturnTy = AST.getLValueReferenceType(T: ReturnTy);
2739 } else {
2740 assert(!IsConstReturn && "There shouldn't be any resource methods with a "
2741 "const ref return value");
2742 ReturnTy = ElemTy;
2743 }
2744 MMB.ReturnTy = ReturnTy;
2745
2746 MMB.addParam(Name: "Index", Ty: IndexTy);
2747
2748 if (NeedsTypedBuiltin)
2749 MMB.callBuiltin(BuiltinName: "__builtin_hlsl_resource_getpointer_typed", ReturnType: ElemPtrTy,
2750 ArgSpecs: PH::Handle, ArgSpecs: PH::_0, ArgSpecs&: ElemTy);
2751 else
2752 MMB.callBuiltin(BuiltinName: "__builtin_hlsl_resource_getpointer", ReturnType: ElemPtrTy, ArgSpecs: PH::Handle,
2753 ArgSpecs: PH::_0);
2754
2755 MMB.dereference(Ptr: PH::LastStmt);
2756 if (TransposeResult)
2757 MMB.callBuiltin(BuiltinName: "__builtin_hlsl_transpose_if_memory_is_row_major", ReturnType: ElemTy,
2758 ArgSpecs: PH::LastStmt, ArgSpecs: getConstantIntExpr(value: 1));
2759 return MMB.finalize();
2760}
2761
2762BuiltinTypeDeclBuilder &
2763BuiltinTypeDeclBuilder::addStoreFunction(DeclarationName &Name, bool IsConst,
2764 QualType ValueTy, bool TransposeArg) {
2765 assert(!Record->isCompleteDefinition() && "record is already complete");
2766 ASTContext &AST = SemaRef.getASTContext();
2767 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2768
2769 BuiltinTypeMethodBuilder MMB(*this, Name, AST.VoidTy, IsConst);
2770
2771 if (ValueTy == AST.DependentTy)
2772 ValueTy = MMB.addTemplateTypeParam(Name: "element_type");
2773 QualType AddrSpaceElemTy =
2774 AST.getAddrSpaceQualType(T: ValueTy, AddressSpace: LangAS::hlsl_device);
2775 QualType ElemPtrTy = AST.getPointerType(T: AddrSpaceElemTy);
2776
2777 MMB.addParam(Name: "Index", Ty: AST.UnsignedIntTy).addParam(Name: "Value", Ty: ValueTy);
2778 if (TransposeArg)
2779 MMB.callBuiltin(BuiltinName: "__builtin_hlsl_transpose_if_memory_is_row_major", ReturnType: ValueTy,
2780 ArgSpecs: PH::_1, ArgSpecs: getConstantIntExpr(value: 0));
2781 MMB.callBuiltin(BuiltinName: "__builtin_hlsl_resource_getpointer_typed", ReturnType: ElemPtrTy,
2782 ArgSpecs: PH::Handle, ArgSpecs: PH::_0, ArgSpecs&: ValueTy)
2783 .dereference(Ptr: PH::LastStmt)
2784 .assign(LHS: PH::LastStmt, RHS: TransposeArg ? PH::LastStmt : PH::_1);
2785 return MMB.finalize();
2786}
2787
2788BuiltinTypeDeclBuilder &
2789BuiltinTypeDeclBuilder::addByteAddressBufferInterlockedMethod(
2790 StringRef MethodName, QualType ValueTy, StringRef BuiltinName,
2791 bool RequiresOriginalValue) {
2792 assert(!Record->isCompleteDefinition() && "record is already complete");
2793 ASTContext &AST = SemaRef.getASTContext();
2794 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2795
2796 // Interlocked atomics operate on a typed slot in the buffer. Compose
2797 // `resource_getpointer_typed` with the scalar `__builtin_hlsl_interlocked_*`
2798 // builtin so backend lowering (DXIL and SPIR-V) can pattern-match a
2799 // resource-pointer atomicrmw.
2800 QualType AddrSpaceElemTy =
2801 AST.getAddrSpaceQualType(T: ValueTy, AddressSpace: LangAS::hlsl_device);
2802 QualType ElemPtrTy = AST.getPointerType(T: AddrSpaceElemTy);
2803
2804 auto BuildOverload = [&](bool WithOriginalValue) {
2805 BuiltinTypeMethodBuilder MMB(*this, MethodName, AST.VoidTy);
2806 MMB.addParam(Name: "Offset", Ty: AST.UnsignedIntTy).addParam(Name: "Value", Ty: ValueTy);
2807 if (WithOriginalValue)
2808 MMB.addParam(Name: "OriginalValue", Ty: ValueTy,
2809 Modifier: HLSLParamModifierAttr::Keyword_out);
2810 MMB.callBuiltin(BuiltinName: "__builtin_hlsl_resource_getpointer_typed", ReturnType: ElemPtrTy,
2811 ArgSpecs: PH::Handle, ArgSpecs: PH::_0, ArgSpecs&: ValueTy)
2812 .dereference(Ptr: PH::LastStmt);
2813 if (WithOriginalValue)
2814 MMB.callBuiltin(BuiltinName, ReturnType: AST.VoidTy, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2);
2815 else
2816 MMB.callBuiltin(BuiltinName, ReturnType: AST.VoidTy, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1);
2817 MMB.finalize();
2818 };
2819
2820 if (!RequiresOriginalValue)
2821 BuildOverload(/*WithOriginalValue=*/false);
2822 BuildOverload(/*WithOriginalValue=*/true);
2823 return *this;
2824}
2825
2826BuiltinTypeDeclBuilder &
2827BuiltinTypeDeclBuilder::addByteAddressBufferInterlockedCompareMethod(
2828 StringRef MethodName, QualType ValueTy, StringRef BuiltinName,
2829 bool WithOriginalValue) {
2830 assert(!Record->isCompleteDefinition() && "record is already complete");
2831 ASTContext &AST = SemaRef.getASTContext();
2832 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2833
2834 QualType AddrSpaceElemTy =
2835 AST.getAddrSpaceQualType(T: ValueTy, AddressSpace: LangAS::hlsl_device);
2836 QualType ElemPtrTy = AST.getPointerType(T: AddrSpaceElemTy);
2837
2838 BuiltinTypeMethodBuilder MMB(*this, MethodName, AST.VoidTy);
2839 MMB.addParam(Name: "Offset", Ty: AST.UnsignedIntTy)
2840 .addParam(Name: "CompareValue", Ty: ValueTy)
2841 .addParam(Name: "Value", Ty: ValueTy);
2842 if (WithOriginalValue)
2843 MMB.addParam(Name: "OriginalValue", Ty: ValueTy, Modifier: HLSLParamModifierAttr::Keyword_out);
2844 MMB.callBuiltin(BuiltinName: "__builtin_hlsl_resource_getpointer_typed", ReturnType: ElemPtrTy,
2845 ArgSpecs: PH::Handle, ArgSpecs: PH::_0, ArgSpecs&: ValueTy)
2846 .dereference(Ptr: PH::LastStmt);
2847 if (WithOriginalValue)
2848 MMB.callBuiltin(BuiltinName, ReturnType: AST.VoidTy, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2,
2849 ArgSpecs: PH::_3);
2850 else
2851 MMB.callBuiltin(BuiltinName, ReturnType: AST.VoidTy, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2);
2852 MMB.finalize();
2853 return *this;
2854}
2855
2856BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() {
2857 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2858 ASTContext &AST = SemaRef.getASTContext();
2859 QualType ElemTy = getHandleElementType();
2860 QualType AddrSpaceElemTy =
2861 AST.getAddrSpaceQualType(T: ElemTy, AddressSpace: LangAS::hlsl_device);
2862 return BuiltinTypeMethodBuilder(*this, "Append", AST.VoidTy)
2863 .addParam(Name: "value", Ty: ElemTy)
2864 .callBuiltin(BuiltinName: "__builtin_hlsl_buffer_update_counter", ReturnType: AST.UnsignedIntTy,
2865 ArgSpecs: PH::CounterHandle, ArgSpecs: getConstantIntExpr(value: 1))
2866 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_getpointer",
2867 ReturnType: AST.getPointerType(T: AddrSpaceElemTy), ArgSpecs: PH::Handle,
2868 ArgSpecs: PH::LastStmt)
2869 .dereference(Ptr: PH::LastStmt)
2870 .assign(LHS: PH::LastStmt, RHS: PH::_0)
2871 .finalize();
2872}
2873
2874BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() {
2875 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2876 ASTContext &AST = SemaRef.getASTContext();
2877 QualType ElemTy = getHandleElementType();
2878 QualType AddrSpaceElemTy =
2879 AST.getAddrSpaceQualType(T: ElemTy, AddressSpace: LangAS::hlsl_device);
2880 return BuiltinTypeMethodBuilder(*this, "Consume", ElemTy)
2881 .callBuiltin(BuiltinName: "__builtin_hlsl_buffer_update_counter", ReturnType: AST.UnsignedIntTy,
2882 ArgSpecs: PH::CounterHandle, ArgSpecs: getConstantIntExpr(value: -1))
2883 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_getpointer",
2884 ReturnType: AST.getPointerType(T: AddrSpaceElemTy), ArgSpecs: PH::Handle,
2885 ArgSpecs: PH::LastStmt)
2886 .dereference(Ptr: PH::LastStmt)
2887 .finalize();
2888}
2889
2890BuiltinTypeDeclBuilder &
2891BuiltinTypeDeclBuilder::addGetDimensionsMethodForBuffer() {
2892 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
2893 ASTContext &AST = SemaRef.getASTContext();
2894 QualType UIntTy = AST.UnsignedIntTy;
2895
2896 QualType HandleTy = getResourceHandleField()->getType();
2897 auto *AttrResTy = cast<HLSLAttributedResourceType>(Val: HandleTy.getTypePtr());
2898
2899 // Structured buffers except {RW}ByteAddressBuffer have overload
2900 // GetDimensions(out uint numStructs, out uint stride).
2901 if (AttrResTy->getAttrs().RawBuffer &&
2902 AttrResTy->getContainedType() != AST.Char8Ty) {
2903 return BuiltinTypeMethodBuilder(*this, "GetDimensions", AST.VoidTy)
2904 .addParam(Name: "numStructs", Ty: UIntTy, Modifier: HLSLParamModifierAttr::Keyword_out)
2905 .addParam(Name: "stride", Ty: UIntTy, Modifier: HLSLParamModifierAttr::Keyword_out)
2906 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_getdimensions_x", ReturnType: QualType(),
2907 ArgSpecs: PH::Handle, ArgSpecs: PH::_0)
2908 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_getstride", ReturnType: QualType(),
2909 ArgSpecs: PH::Handle, ArgSpecs: PH::_1)
2910 .finalize();
2911 }
2912
2913 // Typed buffers and {RW}ByteAddressBuffer have overload
2914 // GetDimensions(out uint dim).
2915 return BuiltinTypeMethodBuilder(*this, "GetDimensions", AST.VoidTy)
2916 .addParam(Name: "dim", Ty: UIntTy, Modifier: HLSLParamModifierAttr::Keyword_out)
2917 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_getdimensions_x", ReturnType: QualType(),
2918 ArgSpecs: PH::Handle, ArgSpecs: PH::_0)
2919 .finalize();
2920}
2921
2922} // namespace hlsl
2923} // namespace clang
2924