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/Expr.h"
20#include "clang/AST/HLSLResource.h"
21#include "clang/AST/Stmt.h"
22#include "clang/AST/Type.h"
23#include "clang/Basic/SourceLocation.h"
24#include "clang/Basic/Specifiers.h"
25#include "clang/Sema/Lookup.h"
26#include "clang/Sema/Sema.h"
27#include "clang/Sema/SemaHLSL.h"
28#include "llvm/ADT/SmallVector.h"
29
30using namespace llvm::hlsl;
31
32namespace clang {
33
34namespace hlsl {
35
36namespace {
37
38static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name) {
39 IdentifierInfo &II =
40 S.getASTContext().Idents.get(Name, TokenCode: tok::TokenKind::identifier);
41 DeclarationNameInfo NameInfo =
42 DeclarationNameInfo(DeclarationName(&II), SourceLocation());
43 LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
44 // AllowBuiltinCreation is false but LookupDirect will create
45 // the builtin when searching the global scope anyways...
46 S.LookupName(R, S: S.getCurScope());
47 // FIXME: If the builtin function was user-declared in global scope,
48 // this assert *will* fail. Should this call LookupBuiltin instead?
49 assert(R.isSingleResult() &&
50 "Since this is a builtin it should always resolve!");
51 return cast<FunctionDecl>(Val: R.getFoundDecl());
52}
53
54static QualType lookupBuiltinType(Sema &S, StringRef Name, DeclContext *DC) {
55 IdentifierInfo &II =
56 S.getASTContext().Idents.get(Name, TokenCode: tok::TokenKind::identifier);
57 LookupResult Result(S, &II, SourceLocation(), Sema::LookupTagName);
58 S.LookupQualifiedName(R&: Result, LookupCtx: DC);
59 assert(!Result.empty() && "Builtin type not found");
60 QualType Ty =
61 S.getASTContext().getTypeDeclType(Decl: Result.getAsSingle<TypeDecl>());
62 S.RequireCompleteType(Loc: SourceLocation(), T: Ty,
63 DiagID: diag::err_tentative_def_incomplete_type);
64 return Ty;
65}
66
67CXXConstructorDecl *lookupCopyConstructor(QualType ResTy) {
68 assert(ResTy->isRecordType() && "not a CXXRecord type");
69 for (auto *CD : ResTy->getAsCXXRecordDecl()->ctors())
70 if (CD->isCopyConstructor())
71 return CD;
72 return nullptr;
73}
74
75ParameterABI
76convertParamModifierToParamABI(HLSLParamModifierAttr::Spelling Modifier) {
77 assert(Modifier != HLSLParamModifierAttr::Spelling::Keyword_in &&
78 "HLSL 'in' parameters modifier cannot be converted to ParameterABI");
79 switch (Modifier) {
80 case HLSLParamModifierAttr::Spelling::Keyword_out:
81 return ParameterABI::HLSLOut;
82 case HLSLParamModifierAttr::Spelling::Keyword_inout:
83 return ParameterABI::HLSLInOut;
84 default:
85 llvm_unreachable("Invalid HLSL parameter modifier");
86 }
87}
88
89QualType getInoutParameterType(ASTContext &AST, QualType Ty) {
90 assert(!Ty->isReferenceType() &&
91 "Pointer and reference types cannot be inout or out parameters");
92 Ty = AST.getLValueReferenceType(T: Ty);
93 Ty.addRestrict();
94 return Ty;
95}
96
97} // namespace
98
99// Builder for template arguments of builtin types. Used internally
100// by BuiltinTypeDeclBuilder.
101struct TemplateParameterListBuilder {
102 BuiltinTypeDeclBuilder &Builder;
103 llvm::SmallVector<NamedDecl *> Params;
104
105 TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB) : Builder(RB) {}
106 ~TemplateParameterListBuilder();
107
108 TemplateParameterListBuilder &
109 addTypeParameter(StringRef Name, QualType DefaultValue = QualType());
110
111 ConceptSpecializationExpr *
112 constructConceptSpecializationExpr(Sema &S, ConceptDecl *CD);
113
114 BuiltinTypeDeclBuilder &finalizeTemplateArgs(ConceptDecl *CD = nullptr);
115};
116
117// Builder for methods or constructors of builtin types. Allows creating methods
118// or constructors of builtin types using the builder pattern like this:
119//
120// BuiltinTypeMethodBuilder(RecordBuilder, "MethodName", ReturnType)
121// .addParam("param_name", Type, InOutModifier)
122// .callBuiltin("builtin_name", BuiltinParams...)
123// .finalize();
124//
125// The builder needs to have all of the parameters before it can create
126// a CXXMethodDecl or CXXConstructorDecl. It collects them in addParam calls and
127// when a first method that builds the body is called or when access to 'this`
128// is needed it creates the CXXMethodDecl/CXXConstructorDecl and ParmVarDecls
129// instances. These can then be referenced from the body building methods.
130// Destructor or an explicit call to finalize() will complete the method
131// definition.
132//
133// The callBuiltin helper method accepts constants via `Expr *` or placeholder
134// value arguments to indicate which function arguments to forward to the
135// builtin.
136//
137// If the method that is being built has a non-void return type the
138// finalize() will create a return statement with the value of the last
139// statement (unless the last statement is already a ReturnStmt or the return
140// value is void).
141struct BuiltinTypeMethodBuilder {
142private:
143 struct Param {
144 const IdentifierInfo &NameII;
145 QualType Ty;
146 HLSLParamModifierAttr::Spelling Modifier;
147 Param(const IdentifierInfo &NameII, QualType Ty,
148 HLSLParamModifierAttr::Spelling Modifier)
149 : NameII(NameII), Ty(Ty), Modifier(Modifier) {}
150 };
151
152 struct LocalVar {
153 StringRef Name;
154 QualType Ty;
155 VarDecl *Decl;
156 LocalVar(StringRef Name, QualType Ty) : Name(Name), Ty(Ty), Decl(nullptr) {}
157 };
158
159 BuiltinTypeDeclBuilder &DeclBuilder;
160 DeclarationName Name;
161 QualType ReturnTy;
162 // method or constructor declaration
163 // (CXXConstructorDecl derives from CXXMethodDecl)
164 CXXMethodDecl *Method;
165 bool IsConst;
166 bool IsCtor;
167 StorageClass SC;
168 llvm::SmallVector<Param> Params;
169 llvm::SmallVector<Stmt *> StmtsList;
170
171 // Argument placeholders, inspired by std::placeholder. These are the indices
172 // of arguments to forward to `callBuiltin` and other method builder methods.
173 // Additional special values are:
174 // Handle - refers to the resource handle.
175 // LastStmt - refers to the last statement in the method body; referencing
176 // LastStmt will remove the statement from the method body since
177 // it will be linked from the new expression being constructed.
178 enum class PlaceHolder {
179 _0,
180 _1,
181 _2,
182 _3,
183 _4,
184 _5,
185 Handle = 128,
186 CounterHandle,
187 LastStmt
188 };
189
190 Expr *convertPlaceholder(PlaceHolder PH);
191 Expr *convertPlaceholder(LocalVar &Var);
192 Expr *convertPlaceholder(Expr *E) { return E; }
193
194public:
195 friend BuiltinTypeDeclBuilder;
196
197 BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, DeclarationName &Name,
198 QualType ReturnTy, bool IsConst = false,
199 bool IsCtor = false, StorageClass SC = SC_None)
200 : DeclBuilder(DB), Name(Name), ReturnTy(ReturnTy), Method(nullptr),
201 IsConst(IsConst), IsCtor(IsCtor), SC(SC) {}
202
203 BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, StringRef NameStr,
204 QualType ReturnTy, bool IsConst = false,
205 bool IsCtor = false, StorageClass SC = SC_None);
206 BuiltinTypeMethodBuilder(const BuiltinTypeMethodBuilder &Other) = delete;
207
208 ~BuiltinTypeMethodBuilder() { finalize(); }
209
210 BuiltinTypeMethodBuilder &
211 operator=(const BuiltinTypeMethodBuilder &Other) = delete;
212
213 BuiltinTypeMethodBuilder &addParam(StringRef Name, QualType Ty,
214 HLSLParamModifierAttr::Spelling Modifier =
215 HLSLParamModifierAttr::Keyword_in);
216 BuiltinTypeMethodBuilder &declareLocalVar(LocalVar &Var);
217 template <typename... Ts>
218 BuiltinTypeMethodBuilder &callBuiltin(StringRef BuiltinName,
219 QualType ReturnType, Ts &&...ArgSpecs);
220 template <typename TLHS, typename TRHS>
221 BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS);
222 template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr);
223
224 template <typename T>
225 BuiltinTypeMethodBuilder &accessHandleFieldOnResource(T ResourceRecord);
226 template <typename ResourceT, typename ValueT>
227 BuiltinTypeMethodBuilder &setHandleFieldOnResource(ResourceT ResourceRecord,
228 ValueT HandleValue);
229 template <typename T>
230 BuiltinTypeMethodBuilder &
231 accessCounterHandleFieldOnResource(T ResourceRecord);
232 template <typename ResourceT, typename ValueT>
233 BuiltinTypeMethodBuilder &
234 setCounterHandleFieldOnResource(ResourceT ResourceRecord, ValueT HandleValue);
235 template <typename T> BuiltinTypeMethodBuilder &returnValue(T ReturnValue);
236 BuiltinTypeMethodBuilder &returnThis();
237 BuiltinTypeDeclBuilder &finalize();
238 Expr *getResourceHandleExpr();
239 Expr *getResourceCounterHandleExpr();
240
241private:
242 void createDecl();
243
244 // Makes sure the declaration is created; should be called before any
245 // statement added to the body or when access to 'this' is needed.
246 void ensureCompleteDecl() {
247 if (!Method)
248 createDecl();
249 }
250
251 template <typename ResourceT, typename ValueT>
252 BuiltinTypeMethodBuilder &setFieldOnResource(ResourceT ResourceRecord,
253 ValueT HandleValue,
254 FieldDecl *HandleField);
255};
256
257TemplateParameterListBuilder::~TemplateParameterListBuilder() {
258 finalizeTemplateArgs();
259}
260
261TemplateParameterListBuilder &
262TemplateParameterListBuilder::addTypeParameter(StringRef Name,
263 QualType DefaultValue) {
264 assert(!Builder.Record->isCompleteDefinition() &&
265 "record is already complete");
266 ASTContext &AST = Builder.SemaRef.getASTContext();
267 unsigned Position = static_cast<unsigned>(Params.size());
268 auto *Decl = TemplateTypeParmDecl::Create(
269 C: AST, DC: Builder.Record->getDeclContext(), KeyLoc: SourceLocation(), NameLoc: SourceLocation(),
270 /* TemplateDepth */ D: 0, P: Position,
271 Id: &AST.Idents.get(Name, TokenCode: tok::TokenKind::identifier),
272 /* Typename */ true,
273 /* ParameterPack */ false,
274 /* HasTypeConstraint*/ false);
275 if (!DefaultValue.isNull())
276 Decl->setDefaultArgument(C: AST,
277 DefArg: Builder.SemaRef.getTrivialTemplateArgumentLoc(
278 Arg: DefaultValue, NTTPType: QualType(), Loc: SourceLocation()));
279
280 Params.emplace_back(Args&: Decl);
281 return *this;
282}
283
284// The concept specialization expression (CSE) constructed in
285// constructConceptSpecializationExpr is constructed so that it
286// matches the CSE that is constructed when parsing the below C++ code:
287//
288// template<typename T>
289// concept is_typed_resource_element_compatible =
290// __builtin_hlsl_typed_resource_element_compatible<T>
291//
292// template<typename element_type> requires
293// is_typed_resource_element_compatible<element_type>
294// struct RWBuffer {
295// element_type Val;
296// };
297//
298// int fn() {
299// RWBuffer<int> Buf;
300// }
301//
302// When dumping the AST and filtering for "RWBuffer", the resulting AST
303// structure is what we're trying to construct below, specifically the
304// CSE portion.
305ConceptSpecializationExpr *
306TemplateParameterListBuilder::constructConceptSpecializationExpr(
307 Sema &S, ConceptDecl *CD) {
308 ASTContext &Context = S.getASTContext();
309 SourceLocation Loc = Builder.Record->getBeginLoc();
310 DeclarationNameInfo DNI(CD->getDeclName(), Loc);
311 NestedNameSpecifierLoc NNSLoc;
312 DeclContext *DC = Builder.Record->getDeclContext();
313 TemplateArgumentListInfo TALI(Loc, Loc);
314
315 // Assume that the concept decl has just one template parameter
316 // This parameter should have been added when CD was constructed
317 // in getTypedBufferConceptDecl
318 assert(CD->getTemplateParameters()->size() == 1 &&
319 "unexpected concept decl parameter count");
320 TemplateTypeParmDecl *ConceptTTPD =
321 dyn_cast<TemplateTypeParmDecl>(Val: CD->getTemplateParameters()->getParam(Idx: 0));
322
323 // this TemplateTypeParmDecl is the template for the resource, and is
324 // used to construct a template argumentthat will be used
325 // to construct the ImplicitConceptSpecializationDecl
326 TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create(
327 C: Context, // AST context
328 DC: Builder.Record->getDeclContext(), // DeclContext
329 KeyLoc: SourceLocation(), NameLoc: SourceLocation(),
330 /*D=*/0, // Depth in the template parameter list
331 /*P=*/0, // Position in the template parameter list
332 /*Id=*/nullptr, // Identifier for 'T'
333 /*Typename=*/true, // Indicates this is a 'typename' or 'class'
334 /*ParameterPack=*/false, // Not a parameter pack
335 /*HasTypeConstraint=*/false // Has no type constraint
336 );
337
338 T->setDeclContext(DC);
339
340 QualType ConceptTType = Context.getTypeDeclType(Decl: ConceptTTPD);
341
342 // this is the 2nd template argument node, on which
343 // the concept constraint is actually being applied: 'element_type'
344 TemplateArgument ConceptTA = TemplateArgument(ConceptTType);
345
346 QualType CSETType = Context.getTypeDeclType(Decl: T);
347
348 // this is the 1st template argument node, which represents
349 // the abstract type that a concept would refer to: 'T'
350 TemplateArgument CSETA = TemplateArgument(CSETType);
351
352 ImplicitConceptSpecializationDecl *ImplicitCSEDecl =
353 ImplicitConceptSpecializationDecl::Create(
354 C: Context, DC: Builder.Record->getDeclContext(), SL: Loc, ConvertedArgs: {CSETA});
355
356 // Constraint satisfaction is used to construct the
357 // ConceptSpecailizationExpr, and represents the 2nd Template Argument,
358 // located at the bottom of the sample AST above.
359 const ConstraintSatisfaction CS(CD, {ConceptTA});
360 TemplateArgumentLoc TAL =
361 S.getTrivialTemplateArgumentLoc(Arg: ConceptTA, NTTPType: QualType(), Loc: SourceLocation());
362
363 TALI.addArgument(Loc: TAL);
364 const ASTTemplateArgumentListInfo *ATALI =
365 ASTTemplateArgumentListInfo::Create(C: Context, List: TALI);
366
367 // In the concept reference, ATALI is what adds the extra
368 // TemplateArgument node underneath CSE
369 ConceptReference *CR =
370 ConceptReference::Create(C: Context, NNS: NNSLoc, TemplateKWLoc: Loc, ConceptNameInfo: DNI, FoundDecl: CD, NamedConcept: CD, ArgsAsWritten: ATALI);
371
372 ConceptSpecializationExpr *CSE =
373 ConceptSpecializationExpr::Create(C: Context, ConceptRef: CR, SpecDecl: ImplicitCSEDecl, Satisfaction: &CS);
374
375 return CSE;
376}
377
378BuiltinTypeDeclBuilder &
379TemplateParameterListBuilder::finalizeTemplateArgs(ConceptDecl *CD) {
380 if (Params.empty())
381 return Builder;
382
383 ASTContext &AST = Builder.SemaRef.Context;
384 ConceptSpecializationExpr *CSE =
385 CD ? constructConceptSpecializationExpr(S&: Builder.SemaRef, CD) : nullptr;
386 auto *ParamList = TemplateParameterList::Create(
387 C: AST, TemplateLoc: SourceLocation(), LAngleLoc: SourceLocation(), Params, RAngleLoc: SourceLocation(), RequiresClause: CSE);
388 Builder.Template = ClassTemplateDecl::Create(
389 C&: AST, DC: Builder.Record->getDeclContext(), L: SourceLocation(),
390 Name: DeclarationName(Builder.Record->getIdentifier()), Params: ParamList,
391 Decl: Builder.Record);
392
393 Builder.Record->setDescribedClassTemplate(Builder.Template);
394 Builder.Template->setImplicit(true);
395 Builder.Template->setLexicalDeclContext(Builder.Record->getDeclContext());
396
397 // NOTE: setPreviousDecl before addDecl so new decl replace old decl when
398 // make visible.
399 Builder.Template->setPreviousDecl(Builder.PrevTemplate);
400 Builder.Record->getDeclContext()->addDecl(D: Builder.Template);
401 Params.clear();
402
403 return Builder;
404}
405
406Expr *BuiltinTypeMethodBuilder::convertPlaceholder(PlaceHolder PH) {
407 if (PH == PlaceHolder::Handle)
408 return getResourceHandleExpr();
409 if (PH == PlaceHolder::CounterHandle)
410 return getResourceCounterHandleExpr();
411
412 if (PH == PlaceHolder::LastStmt) {
413 assert(!StmtsList.empty() && "no statements in the list");
414 Stmt *LastStmt = StmtsList.pop_back_val();
415 assert(isa<ValueStmt>(LastStmt) && "last statement does not have a value");
416 return cast<ValueStmt>(Val: LastStmt)->getExprStmt();
417 }
418
419 // All other placeholders are parameters (_N), and can be loaded as an
420 // LValue. It needs to be an LValue if the result expression will be used as
421 // the actual parameter for an out parameter. The dimension builtins are an
422 // example where this happens.
423 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
424 ParmVarDecl *ParamDecl = Method->getParamDecl(i: static_cast<unsigned>(PH));
425 return DeclRefExpr::Create(
426 Context: AST, QualifierLoc: NestedNameSpecifierLoc(), TemplateKWLoc: SourceLocation(), D: ParamDecl, RefersToEnclosingVariableOrCapture: false,
427 NameInfo: DeclarationNameInfo(ParamDecl->getDeclName(), SourceLocation()),
428 T: ParamDecl->getType().getNonReferenceType(), VK: VK_LValue);
429}
430
431Expr *BuiltinTypeMethodBuilder::convertPlaceholder(LocalVar &Var) {
432 VarDecl *VD = Var.Decl;
433 assert(VD && "local variable is not declared");
434 return DeclRefExpr::Create(
435 Context: VD->getASTContext(), QualifierLoc: NestedNameSpecifierLoc(), TemplateKWLoc: SourceLocation(), D: VD,
436 RefersToEnclosingVariableOrCapture: false, NameInfo: DeclarationNameInfo(VD->getDeclName(), SourceLocation()),
437 T: VD->getType(), VK: VK_LValue);
438}
439
440BuiltinTypeMethodBuilder::BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB,
441 StringRef NameStr,
442 QualType ReturnTy,
443 bool IsConst, bool IsCtor,
444 StorageClass SC)
445 : DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst),
446 IsCtor(IsCtor), SC(SC) {
447
448 assert((!NameStr.empty() || IsCtor) && "method needs a name");
449 assert(((IsCtor && !IsConst) || !IsCtor) && "constructor cannot be const");
450
451 ASTContext &AST = DB.SemaRef.getASTContext();
452 if (IsCtor) {
453 Name = AST.DeclarationNames.getCXXConstructorName(
454 Ty: AST.getCanonicalTagType(TD: DB.Record));
455 } else {
456 const IdentifierInfo &II =
457 AST.Idents.get(Name: NameStr, TokenCode: tok::TokenKind::identifier);
458 Name = DeclarationName(&II);
459 }
460}
461
462BuiltinTypeMethodBuilder &
463BuiltinTypeMethodBuilder::addParam(StringRef Name, QualType Ty,
464 HLSLParamModifierAttr::Spelling Modifier) {
465 assert(Method == nullptr && "Cannot add param, method already created");
466 const IdentifierInfo &II = DeclBuilder.SemaRef.getASTContext().Idents.get(
467 Name, TokenCode: tok::TokenKind::identifier);
468 Params.emplace_back(Args: II, Args&: Ty, Args&: Modifier);
469 return *this;
470}
471
472void BuiltinTypeMethodBuilder::createDecl() {
473 assert(Method == nullptr && "Method or constructor is already created");
474
475 // create function prototype
476 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
477 SmallVector<QualType> ParamTypes;
478 SmallVector<FunctionType::ExtParameterInfo> ParamExtInfos(Params.size());
479 uint32_t ArgIndex = 0;
480
481 // Create function prototype.
482 bool UseParamExtInfo = false;
483 for (Param &MP : Params) {
484 if (MP.Modifier != HLSLParamModifierAttr::Keyword_in) {
485 UseParamExtInfo = true;
486 FunctionType::ExtParameterInfo &PI = ParamExtInfos[ArgIndex];
487 ParamExtInfos[ArgIndex] =
488 PI.withABI(kind: convertParamModifierToParamABI(Modifier: MP.Modifier));
489 if (!MP.Ty->isDependentType())
490 MP.Ty = getInoutParameterType(AST, Ty: MP.Ty);
491 }
492 ParamTypes.emplace_back(Args&: MP.Ty);
493 ++ArgIndex;
494 }
495
496 FunctionProtoType::ExtProtoInfo ExtInfo;
497 if (UseParamExtInfo)
498 ExtInfo.ExtParameterInfos = ParamExtInfos.data();
499 if (IsConst)
500 ExtInfo.TypeQuals.addConst();
501
502 QualType FuncTy = AST.getFunctionType(ResultTy: ReturnTy, Args: ParamTypes, EPI: ExtInfo);
503
504 // Create method or constructor declaration.
505 auto *TSInfo = AST.getTrivialTypeSourceInfo(T: FuncTy, Loc: SourceLocation());
506 DeclarationNameInfo NameInfo = DeclarationNameInfo(Name, SourceLocation());
507 if (IsCtor)
508 Method = CXXConstructorDecl::Create(
509 C&: AST, RD: DeclBuilder.Record, StartLoc: SourceLocation(), NameInfo, T: FuncTy, TInfo: TSInfo,
510 ES: ExplicitSpecifier(), UsesFPIntrin: false, /*IsInline=*/isInline: true, isImplicitlyDeclared: false,
511 ConstexprKind: ConstexprSpecKind::Unspecified);
512 else
513 Method = CXXMethodDecl::Create(
514 C&: AST, RD: DeclBuilder.Record, StartLoc: SourceLocation(), NameInfo, T: FuncTy, TInfo: TSInfo, SC,
515 UsesFPIntrin: false, isInline: true, ConstexprKind: ConstexprSpecKind::Unspecified, EndLocation: SourceLocation());
516
517 // Create params & set them to the method/constructor and function prototype.
518 SmallVector<ParmVarDecl *> ParmDecls;
519 unsigned CurScopeDepth = DeclBuilder.SemaRef.getCurScope()->getDepth();
520 auto FnProtoLoc =
521 Method->getTypeSourceInfo()->getTypeLoc().getAs<FunctionProtoTypeLoc>();
522 for (int I = 0, E = Params.size(); I != E; I++) {
523 Param &MP = Params[I];
524 ParmVarDecl *Parm = ParmVarDecl::Create(
525 C&: AST, DC: Method->getDeclContext(), StartLoc: SourceLocation(), IdLoc: SourceLocation(),
526 Id: &MP.NameII, T: MP.Ty,
527 TInfo: AST.getTrivialTypeSourceInfo(T: MP.Ty, Loc: SourceLocation()), S: SC_None,
528 DefArg: nullptr);
529 if (MP.Modifier != HLSLParamModifierAttr::Keyword_in) {
530 auto *Mod =
531 HLSLParamModifierAttr::Create(Ctx&: AST, Range: SourceRange(), S: MP.Modifier);
532 Parm->addAttr(A: Mod);
533 }
534 Parm->setScopeInfo(scopeDepth: CurScopeDepth, parameterIndex: I);
535 ParmDecls.push_back(Elt: Parm);
536 FnProtoLoc.setParam(i: I, VD: Parm);
537 }
538 Method->setParams({ParmDecls});
539}
540
541Expr *BuiltinTypeMethodBuilder::getResourceHandleExpr() {
542 ensureCompleteDecl();
543
544 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
545 CXXThisExpr *This = CXXThisExpr::Create(
546 Ctx: AST, L: SourceLocation(), Ty: Method->getFunctionObjectParameterType(), IsImplicit: true);
547 FieldDecl *HandleField = DeclBuilder.getResourceHandleField();
548 return MemberExpr::CreateImplicit(C: AST, Base: This, IsArrow: false, MemberDecl: HandleField,
549 T: HandleField->getType(), VK: VK_LValue,
550 OK: OK_Ordinary);
551}
552
553Expr *BuiltinTypeMethodBuilder::getResourceCounterHandleExpr() {
554 ensureCompleteDecl();
555
556 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
557 CXXThisExpr *This = CXXThisExpr::Create(
558 Ctx: AST, L: SourceLocation(), Ty: Method->getFunctionObjectParameterType(), IsImplicit: true);
559 FieldDecl *HandleField = DeclBuilder.getResourceCounterHandleField();
560 return MemberExpr::CreateImplicit(C: AST, Base: This, IsArrow: false, MemberDecl: HandleField,
561 T: HandleField->getType(), VK: VK_LValue,
562 OK: OK_Ordinary);
563}
564
565BuiltinTypeMethodBuilder &
566BuiltinTypeMethodBuilder::declareLocalVar(LocalVar &Var) {
567 ensureCompleteDecl();
568
569 assert(Var.Decl == nullptr && "local variable is already declared");
570
571 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
572 Var.Decl = VarDecl::Create(
573 C&: AST, DC: Method, StartLoc: SourceLocation(), IdLoc: SourceLocation(),
574 Id: &AST.Idents.get(Name: Var.Name, TokenCode: tok::TokenKind::identifier), T: Var.Ty,
575 TInfo: AST.getTrivialTypeSourceInfo(T: Var.Ty, Loc: SourceLocation()), S: SC_None);
576 DeclStmt *DS = new (AST) clang::DeclStmt(DeclGroupRef(Var.Decl),
577 SourceLocation(), SourceLocation());
578 StmtsList.push_back(Elt: DS);
579 return *this;
580}
581
582BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::returnThis() {
583 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
584 CXXThisExpr *ThisExpr = CXXThisExpr::Create(
585 Ctx: AST, L: SourceLocation(), Ty: Method->getFunctionObjectParameterType(),
586 /*IsImplicit=*/true);
587 StmtsList.push_back(Elt: ThisExpr);
588 return *this;
589}
590
591template <typename... Ts>
592BuiltinTypeMethodBuilder &
593BuiltinTypeMethodBuilder::callBuiltin(StringRef BuiltinName,
594 QualType ReturnType, Ts &&...ArgSpecs) {
595 ensureCompleteDecl();
596
597 std::array<Expr *, sizeof...(ArgSpecs)> Args{
598 convertPlaceholder(std::forward<Ts>(ArgSpecs))...};
599
600 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
601 FunctionDecl *FD = lookupBuiltinFunction(S&: DeclBuilder.SemaRef, Name: BuiltinName);
602 DeclRefExpr *DRE = DeclRefExpr::Create(
603 Context: AST, QualifierLoc: NestedNameSpecifierLoc(), TemplateKWLoc: SourceLocation(), D: FD, RefersToEnclosingVariableOrCapture: false,
604 NameInfo: FD->getNameInfo(), T: AST.BuiltinFnTy, VK: VK_PRValue);
605
606 ExprResult Call = DeclBuilder.SemaRef.BuildCallExpr(
607 /*Scope=*/S: nullptr, Fn: DRE, LParenLoc: SourceLocation(),
608 ArgExprs: MultiExprArg(Args.data(), Args.size()), RParenLoc: SourceLocation());
609 assert(!Call.isInvalid() && "Call to builtin cannot fail!");
610 Expr *E = Call.get();
611
612 if (!ReturnType.isNull() &&
613 !AST.hasSameUnqualifiedType(T1: ReturnType, T2: E->getType())) {
614 ExprResult CastResult = DeclBuilder.SemaRef.BuildCStyleCastExpr(
615 LParenLoc: SourceLocation(), Ty: AST.getTrivialTypeSourceInfo(T: ReturnType),
616 RParenLoc: SourceLocation(), Op: E);
617 assert(!CastResult.isInvalid() && "Cast cannot fail!");
618 E = CastResult.get();
619 }
620
621 StmtsList.push_back(Elt: E);
622 return *this;
623}
624
625template <typename TLHS, typename TRHS>
626BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::assign(TLHS LHS, TRHS RHS) {
627 Expr *LHSExpr = convertPlaceholder(LHS);
628 Expr *RHSExpr = convertPlaceholder(RHS);
629 Stmt *AssignStmt = BinaryOperator::Create(
630 C: DeclBuilder.SemaRef.getASTContext(), lhs: LHSExpr, rhs: RHSExpr, opc: BO_Assign,
631 ResTy: LHSExpr->getType(), VK: ExprValueKind::VK_PRValue,
632 OK: ExprObjectKind::OK_Ordinary, opLoc: SourceLocation(), FPFeatures: FPOptionsOverride());
633 StmtsList.push_back(Elt: AssignStmt);
634 return *this;
635}
636
637template <typename T>
638BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::dereference(T Ptr) {
639 Expr *PtrExpr = convertPlaceholder(Ptr);
640 Expr *Deref =
641 UnaryOperator::Create(C: DeclBuilder.SemaRef.getASTContext(), input: PtrExpr,
642 opc: UO_Deref, type: PtrExpr->getType()->getPointeeType(),
643 VK: VK_PRValue, OK: OK_Ordinary, l: SourceLocation(),
644 /*CanOverflow=*/false, FPFeatures: FPOptionsOverride());
645 StmtsList.push_back(Elt: Deref);
646 return *this;
647}
648
649template <typename T>
650BuiltinTypeMethodBuilder &
651BuiltinTypeMethodBuilder::accessHandleFieldOnResource(T ResourceRecord) {
652 ensureCompleteDecl();
653
654 Expr *ResourceExpr = convertPlaceholder(ResourceRecord);
655 auto *ResourceTypeDecl = ResourceExpr->getType()->getAsCXXRecordDecl();
656
657 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
658 FieldDecl *HandleField = nullptr;
659
660 if (ResourceTypeDecl == DeclBuilder.Record)
661 HandleField = DeclBuilder.getResourceHandleField();
662 else {
663 IdentifierInfo &II = AST.Idents.get(Name: "__handle");
664 for (auto *Decl : ResourceTypeDecl->lookup(Name: &II)) {
665 if ((HandleField = dyn_cast<FieldDecl>(Val: Decl)))
666 break;
667 }
668 assert(HandleField && "Resource handle field not found");
669 }
670
671 MemberExpr *HandleExpr = MemberExpr::CreateImplicit(
672 C: AST, Base: ResourceExpr, IsArrow: false, MemberDecl: HandleField, T: HandleField->getType(), VK: VK_LValue,
673 OK: OK_Ordinary);
674 StmtsList.push_back(Elt: HandleExpr);
675 return *this;
676}
677
678template <typename ResourceT, typename ValueT>
679BuiltinTypeMethodBuilder &
680BuiltinTypeMethodBuilder::setHandleFieldOnResource(ResourceT ResourceRecord,
681 ValueT HandleValue) {
682 return setFieldOnResource(ResourceRecord, HandleValue,
683 DeclBuilder.getResourceHandleField());
684}
685
686template <typename ResourceT, typename ValueT>
687BuiltinTypeMethodBuilder &
688BuiltinTypeMethodBuilder::setCounterHandleFieldOnResource(
689 ResourceT ResourceRecord, ValueT HandleValue) {
690 return setFieldOnResource(ResourceRecord, HandleValue,
691 DeclBuilder.getResourceCounterHandleField());
692}
693
694template <typename ResourceT, typename ValueT>
695BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::setFieldOnResource(
696 ResourceT ResourceRecord, ValueT HandleValue, FieldDecl *HandleField) {
697 ensureCompleteDecl();
698
699 Expr *ResourceExpr = convertPlaceholder(ResourceRecord);
700 assert(ResourceExpr->getType()->getAsCXXRecordDecl() ==
701 HandleField->getParent() &&
702 "Getting the field from the wrong resource type.");
703
704 Expr *HandleValueExpr = convertPlaceholder(HandleValue);
705
706 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
707 MemberExpr *HandleMemberExpr = MemberExpr::CreateImplicit(
708 C: AST, Base: ResourceExpr, IsArrow: false, MemberDecl: HandleField, T: HandleField->getType(), VK: VK_LValue,
709 OK: OK_Ordinary);
710 Stmt *AssignStmt = BinaryOperator::Create(
711 C: DeclBuilder.SemaRef.getASTContext(), lhs: HandleMemberExpr, rhs: HandleValueExpr,
712 opc: BO_Assign, ResTy: HandleMemberExpr->getType(), VK: ExprValueKind::VK_PRValue,
713 OK: ExprObjectKind::OK_Ordinary, opLoc: SourceLocation(), FPFeatures: FPOptionsOverride());
714 StmtsList.push_back(Elt: AssignStmt);
715 return *this;
716}
717
718template <typename T>
719BuiltinTypeMethodBuilder &
720BuiltinTypeMethodBuilder::accessCounterHandleFieldOnResource(T ResourceRecord) {
721 ensureCompleteDecl();
722
723 Expr *ResourceExpr = convertPlaceholder(ResourceRecord);
724 assert(ResourceExpr->getType()->getAsCXXRecordDecl() == DeclBuilder.Record &&
725 "Getting the field from the wrong resource type.");
726
727 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
728 FieldDecl *HandleField = DeclBuilder.getResourceCounterHandleField();
729 MemberExpr *HandleExpr = MemberExpr::CreateImplicit(
730 C: AST, Base: ResourceExpr, IsArrow: false, MemberDecl: HandleField, T: HandleField->getType(), VK: VK_LValue,
731 OK: OK_Ordinary);
732 StmtsList.push_back(Elt: HandleExpr);
733 return *this;
734}
735
736template <typename T>
737BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::returnValue(T ReturnValue) {
738 ensureCompleteDecl();
739
740 Expr *ReturnValueExpr = convertPlaceholder(ReturnValue);
741 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
742
743 QualType Ty = ReturnValueExpr->getType();
744 if (Ty->isRecordType()) {
745 // For record types, create a call to copy constructor to ensure proper copy
746 // semantics.
747 auto *ICE =
748 ImplicitCastExpr::Create(Context: AST, T: Ty.withConst(), Kind: CK_NoOp, Operand: ReturnValueExpr,
749 BasePath: nullptr, Cat: VK_XValue, FPO: FPOptionsOverride());
750 CXXConstructorDecl *CD = lookupCopyConstructor(ResTy: Ty);
751 assert(CD && "no copy constructor found");
752 ReturnValueExpr = CXXConstructExpr::Create(
753 Ctx: AST, Ty, Loc: SourceLocation(), Ctor: CD, /*Elidable=*/false, Args: {ICE},
754 /*HadMultipleCandidates=*/false, /*ListInitialization=*/false,
755 /*StdInitListInitialization=*/false,
756 /*ZeroInitListInitialization=*/ZeroInitialization: false, ConstructKind: CXXConstructionKind::Complete,
757 ParenOrBraceRange: SourceRange());
758 }
759 StmtsList.push_back(
760 Elt: ReturnStmt::Create(Ctx: AST, RL: SourceLocation(), E: ReturnValueExpr, NRVOCandidate: nullptr));
761 return *this;
762}
763
764BuiltinTypeDeclBuilder &BuiltinTypeMethodBuilder::finalize() {
765 assert(!DeclBuilder.Record->isCompleteDefinition() &&
766 "record is already complete");
767
768 ensureCompleteDecl();
769
770 if (!Method->hasBody()) {
771 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
772 assert((ReturnTy == AST.VoidTy || !StmtsList.empty()) &&
773 "nothing to return from non-void method");
774 if (ReturnTy != AST.VoidTy) {
775 if (Expr *LastExpr = dyn_cast<Expr>(Val: StmtsList.back())) {
776 assert(AST.hasSameUnqualifiedType(LastExpr->getType(),
777 ReturnTy.getNonReferenceType()) &&
778 "Return type of the last statement must match the return type "
779 "of the method");
780 if (!isa<ReturnStmt>(Val: LastExpr)) {
781 StmtsList.pop_back();
782 StmtsList.push_back(
783 Elt: ReturnStmt::Create(Ctx: AST, RL: SourceLocation(), E: LastExpr, NRVOCandidate: nullptr));
784 }
785 }
786 }
787
788 Method->setBody(CompoundStmt::Create(C: AST, Stmts: StmtsList, FPFeatures: FPOptionsOverride(),
789 LB: SourceLocation(), RB: SourceLocation()));
790 Method->setLexicalDeclContext(DeclBuilder.Record);
791 Method->setAccess(AS_public);
792 Method->addAttr(A: AlwaysInlineAttr::CreateImplicit(
793 Ctx&: AST, Range: SourceRange(), S: AlwaysInlineAttr::CXX11_clang_always_inline));
794 DeclBuilder.Record->addDecl(D: Method);
795 }
796 return DeclBuilder;
797}
798
799BuiltinTypeDeclBuilder::BuiltinTypeDeclBuilder(Sema &SemaRef, CXXRecordDecl *R)
800 : SemaRef(SemaRef), Record(R) {
801 Record->startDefinition();
802 Template = Record->getDescribedClassTemplate();
803}
804
805BuiltinTypeDeclBuilder::BuiltinTypeDeclBuilder(Sema &SemaRef,
806 NamespaceDecl *Namespace,
807 StringRef Name)
808 : SemaRef(SemaRef), HLSLNamespace(Namespace) {
809 ASTContext &AST = SemaRef.getASTContext();
810 IdentifierInfo &II = AST.Idents.get(Name, TokenCode: tok::TokenKind::identifier);
811
812 LookupResult Result(SemaRef, &II, SourceLocation(), Sema::LookupTagName);
813 CXXRecordDecl *PrevDecl = nullptr;
814 if (SemaRef.LookupQualifiedName(R&: Result, LookupCtx: HLSLNamespace)) {
815 // Declaration already exists (from precompiled headers)
816 NamedDecl *Found = Result.getFoundDecl();
817 if (auto *TD = dyn_cast<ClassTemplateDecl>(Val: Found)) {
818 PrevDecl = TD->getTemplatedDecl();
819 PrevTemplate = TD;
820 } else
821 PrevDecl = dyn_cast<CXXRecordDecl>(Val: Found);
822 assert(PrevDecl && "Unexpected lookup result type.");
823 }
824
825 if (PrevDecl && PrevDecl->isCompleteDefinition()) {
826 Record = PrevDecl;
827 Template = PrevTemplate;
828 return;
829 }
830
831 Record =
832 CXXRecordDecl::Create(C: AST, TK: TagDecl::TagKind::Class, DC: HLSLNamespace,
833 StartLoc: SourceLocation(), IdLoc: SourceLocation(), Id: &II, PrevDecl);
834 Record->setImplicit(true);
835 Record->setLexicalDeclContext(HLSLNamespace);
836 Record->setHasExternalLexicalStorage();
837
838 // Don't let anyone derive from built-in types.
839 Record->addAttr(
840 A: FinalAttr::CreateImplicit(Ctx&: AST, Range: SourceRange(), S: FinalAttr::Keyword_final));
841}
842
843BuiltinTypeDeclBuilder::~BuiltinTypeDeclBuilder() {
844 if (HLSLNamespace && !Template && Record->getDeclContext() == HLSLNamespace)
845 HLSLNamespace->addDecl(D: Record);
846}
847
848BuiltinTypeDeclBuilder &
849BuiltinTypeDeclBuilder::addMemberVariable(StringRef Name, QualType Type,
850 llvm::ArrayRef<Attr *> Attrs,
851 AccessSpecifier Access) {
852 assert(!Record->isCompleteDefinition() && "record is already complete");
853 assert(Record->isBeingDefined() &&
854 "Definition must be started before adding members!");
855 ASTContext &AST = Record->getASTContext();
856
857 IdentifierInfo &II = AST.Idents.get(Name, TokenCode: tok::TokenKind::identifier);
858 TypeSourceInfo *MemTySource =
859 AST.getTrivialTypeSourceInfo(T: Type, Loc: SourceLocation());
860 auto *Field = FieldDecl::Create(
861 C: AST, DC: Record, StartLoc: SourceLocation(), IdLoc: SourceLocation(), Id: &II, T: Type, TInfo: MemTySource,
862 BW: nullptr, Mutable: false, InitStyle: InClassInitStyle::ICIS_NoInit);
863 Field->setAccess(Access);
864 Field->setImplicit(true);
865 for (Attr *A : Attrs) {
866 if (A)
867 Field->addAttr(A);
868 }
869
870 Record->addDecl(D: Field);
871 Fields[Name] = Field;
872 return *this;
873}
874
875BuiltinTypeDeclBuilder &
876BuiltinTypeDeclBuilder::addBufferHandles(ResourceClass RC, bool IsROV,
877 bool RawBuffer, bool HasCounter,
878 AccessSpecifier Access) {
879 addHandleMember(RC, RD: ResourceDimension::Unknown, IsROV, RawBuffer, Access);
880 if (HasCounter)
881 addCounterHandleMember(RC, IsROV, RawBuffer, Access);
882 return *this;
883}
884
885BuiltinTypeDeclBuilder &
886BuiltinTypeDeclBuilder::addTextureHandle(ResourceClass RC, bool IsROV,
887 ResourceDimension RD,
888 AccessSpecifier Access) {
889 addHandleMember(RC, RD, IsROV, /*RawBuffer=*/false, Access);
890 return *this;
891}
892
893BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addSamplerHandle() {
894 addHandleMember(RC: ResourceClass::Sampler, RD: ResourceDimension::Unknown,
895 /*IsROV=*/false, /*RawBuffer=*/false);
896 return *this;
897}
898
899BuiltinTypeDeclBuilder &
900BuiltinTypeDeclBuilder::addHandleMember(ResourceClass RC, ResourceDimension RD,
901 bool IsROV, bool RawBuffer,
902 AccessSpecifier Access) {
903 return addResourceMember(MemberName: "__handle", RC, RD, IsROV, RawBuffer,
904 /*IsCounter=*/false, Access);
905}
906
907BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCounterHandleMember(
908 ResourceClass RC, bool IsROV, bool RawBuffer, AccessSpecifier Access) {
909 return addResourceMember(MemberName: "__counter_handle", RC, RD: ResourceDimension::Unknown,
910 IsROV, RawBuffer,
911 /*IsCounter=*/true, Access);
912}
913
914BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addResourceMember(
915 StringRef MemberName, ResourceClass RC, ResourceDimension RD, bool IsROV,
916 bool RawBuffer, bool IsCounter, AccessSpecifier Access) {
917 assert(!Record->isCompleteDefinition() && "record is already complete");
918
919 ASTContext &Ctx = SemaRef.getASTContext();
920 TypeSourceInfo *ElementTypeInfo =
921 Ctx.getTrivialTypeSourceInfo(T: getHandleElementType(), Loc: SourceLocation());
922
923 // add handle member with resource type attributes
924 QualType AttributedResTy = QualType();
925 SmallVector<const Attr *> Attrs = {
926 HLSLResourceClassAttr::CreateImplicit(Ctx, ResourceClass: RC),
927 IsROV ? HLSLROVAttr::CreateImplicit(Ctx) : nullptr,
928 RawBuffer ? HLSLRawBufferAttr::CreateImplicit(Ctx) : nullptr,
929 RD != ResourceDimension::Unknown
930 ? HLSLResourceDimensionAttr::CreateImplicit(Ctx, Dimension: RD)
931 : nullptr,
932 ElementTypeInfo && RC != ResourceClass::Sampler
933 ? HLSLContainedTypeAttr::CreateImplicit(Ctx, Type: ElementTypeInfo)
934 : nullptr};
935 if (IsCounter)
936 Attrs.push_back(Elt: HLSLIsCounterAttr::CreateImplicit(Ctx));
937
938 if (CreateHLSLAttributedResourceType(S&: SemaRef, Wrapped: Ctx.HLSLResourceTy, AttrList: Attrs,
939 ResType&: AttributedResTy))
940 addMemberVariable(Name: MemberName, Type: AttributedResTy, Attrs: {}, Access);
941 return *this;
942}
943
944// Adds default constructor to the resource class:
945// Resource::Resource()
946BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDefaultHandleConstructor() {
947 assert(!Record->isCompleteDefinition() && "record is already complete");
948
949 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
950 QualType HandleType = getResourceHandleField()->getType();
951 return BuiltinTypeMethodBuilder(*this, "", SemaRef.getASTContext().VoidTy,
952 false, true)
953 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_uninitializedhandle", ReturnType: HandleType,
954 ArgSpecs: PH::Handle)
955 .assign(LHS: PH::Handle, RHS: PH::LastStmt)
956 .finalize();
957}
958
959BuiltinTypeDeclBuilder &
960BuiltinTypeDeclBuilder::addStaticInitializationFunctions(bool HasCounter) {
961 if (HasCounter) {
962 addCreateFromBindingWithImplicitCounter();
963 addCreateFromImplicitBindingWithImplicitCounter();
964 } else {
965 addCreateFromBinding();
966 addCreateFromImplicitBinding();
967 }
968 return *this;
969}
970
971// Adds static method that initializes resource from binding:
972//
973// static Resource<T> __createFromBinding(unsigned registerNo,
974// unsigned spaceNo, int range,
975// unsigned index, const char *name) {
976// Resource<T> tmp;
977// tmp.__handle = __builtin_hlsl_resource_handlefrombinding(
978// tmp.__handle, registerNo, spaceNo,
979// range, index, name);
980// return tmp;
981// }
982BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCreateFromBinding() {
983 assert(!Record->isCompleteDefinition() && "record is already complete");
984
985 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
986 ASTContext &AST = SemaRef.getASTContext();
987 QualType HandleType = getResourceHandleField()->getType();
988 QualType RecordType = AST.getTypeDeclType(Decl: cast<TypeDecl>(Val: Record));
989 BuiltinTypeMethodBuilder::LocalVar TmpVar("tmp", RecordType);
990
991 return BuiltinTypeMethodBuilder(*this, "__createFromBinding", RecordType,
992 false, false, SC_Static)
993 .addParam(Name: "registerNo", Ty: AST.UnsignedIntTy)
994 .addParam(Name: "spaceNo", Ty: AST.UnsignedIntTy)
995 .addParam(Name: "range", Ty: AST.IntTy)
996 .addParam(Name: "index", Ty: AST.UnsignedIntTy)
997 .addParam(Name: "name", Ty: AST.getPointerType(T: AST.CharTy.withConst()))
998 .declareLocalVar(Var&: TmpVar)
999 .accessHandleFieldOnResource(ResourceRecord: TmpVar)
1000 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_handlefrombinding", ReturnType: HandleType,
1001 ArgSpecs: PH::LastStmt, ArgSpecs: PH::_0, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3, ArgSpecs: PH::_4)
1002 .setHandleFieldOnResource(ResourceRecord: TmpVar, HandleValue: PH::LastStmt)
1003 .returnValue(ReturnValue: TmpVar)
1004 .finalize();
1005}
1006
1007// Adds static method that initializes resource from binding:
1008//
1009// static Resource<T> __createFromImplicitBinding(unsigned orderId,
1010// unsigned spaceNo, int range,
1011// unsigned index,
1012// const char *name) {
1013// Resource<T> tmp;
1014// tmp.__handle = __builtin_hlsl_resource_handlefromimplicitbinding(
1015// tmp.__handle, spaceNo,
1016// range, index, orderId, name);
1017// return tmp;
1018// }
1019BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCreateFromImplicitBinding() {
1020 assert(!Record->isCompleteDefinition() && "record is already complete");
1021
1022 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1023 ASTContext &AST = SemaRef.getASTContext();
1024 QualType HandleType = getResourceHandleField()->getType();
1025 QualType RecordType = AST.getTypeDeclType(Decl: cast<TypeDecl>(Val: Record));
1026 BuiltinTypeMethodBuilder::LocalVar TmpVar("tmp", RecordType);
1027
1028 return BuiltinTypeMethodBuilder(*this, "__createFromImplicitBinding",
1029 RecordType, false, false, SC_Static)
1030 .addParam(Name: "orderId", Ty: AST.UnsignedIntTy)
1031 .addParam(Name: "spaceNo", Ty: AST.UnsignedIntTy)
1032 .addParam(Name: "range", Ty: AST.IntTy)
1033 .addParam(Name: "index", Ty: AST.UnsignedIntTy)
1034 .addParam(Name: "name", Ty: AST.getPointerType(T: AST.CharTy.withConst()))
1035 .declareLocalVar(Var&: TmpVar)
1036 .accessHandleFieldOnResource(ResourceRecord: TmpVar)
1037 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_handlefromimplicitbinding",
1038 ReturnType: HandleType, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_0, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3,
1039 ArgSpecs: PH::_4)
1040 .setHandleFieldOnResource(ResourceRecord: TmpVar, HandleValue: PH::LastStmt)
1041 .returnValue(ReturnValue: TmpVar)
1042 .finalize();
1043}
1044
1045// Adds static method that initializes resource from binding:
1046//
1047// static Resource<T>
1048// __createFromBindingWithImplicitCounter(unsigned registerNo,
1049// unsigned spaceNo, int range,
1050// unsigned index, const char *name,
1051// unsigned counterOrderId) {
1052// Resource<T> tmp;
1053// tmp.__handle = __builtin_hlsl_resource_handlefrombinding(
1054// tmp.__handle, registerNo, spaceNo, range, index, name);
1055// tmp.__counter_handle =
1056// __builtin_hlsl_resource_counterhandlefromimplicitbinding(
1057// tmp.__handle, counterOrderId, spaceNo);
1058// return tmp;
1059// }
1060BuiltinTypeDeclBuilder &
1061BuiltinTypeDeclBuilder::addCreateFromBindingWithImplicitCounter() {
1062 assert(!Record->isCompleteDefinition() && "record is already complete");
1063
1064 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1065 ASTContext &AST = SemaRef.getASTContext();
1066 QualType HandleType = getResourceHandleField()->getType();
1067 QualType CounterHandleType = getResourceCounterHandleField()->getType();
1068 QualType RecordType = AST.getTypeDeclType(Decl: cast<TypeDecl>(Val: Record));
1069 BuiltinTypeMethodBuilder::LocalVar TmpVar("tmp", RecordType);
1070
1071 return BuiltinTypeMethodBuilder(*this,
1072 "__createFromBindingWithImplicitCounter",
1073 RecordType, false, false, SC_Static)
1074 .addParam(Name: "registerNo", Ty: AST.UnsignedIntTy)
1075 .addParam(Name: "spaceNo", Ty: AST.UnsignedIntTy)
1076 .addParam(Name: "range", Ty: AST.IntTy)
1077 .addParam(Name: "index", Ty: AST.UnsignedIntTy)
1078 .addParam(Name: "name", Ty: AST.getPointerType(T: AST.CharTy.withConst()))
1079 .addParam(Name: "counterOrderId", Ty: AST.UnsignedIntTy)
1080 .declareLocalVar(Var&: TmpVar)
1081 .accessHandleFieldOnResource(ResourceRecord: TmpVar)
1082 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_handlefrombinding", ReturnType: HandleType,
1083 ArgSpecs: PH::LastStmt, ArgSpecs: PH::_0, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3, ArgSpecs: PH::_4)
1084 .setHandleFieldOnResource(ResourceRecord: TmpVar, HandleValue: PH::LastStmt)
1085 .accessHandleFieldOnResource(ResourceRecord: TmpVar)
1086 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_counterhandlefromimplicitbinding",
1087 ReturnType: CounterHandleType, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_5, ArgSpecs: PH::_1)
1088 .setCounterHandleFieldOnResource(ResourceRecord: TmpVar, HandleValue: PH::LastStmt)
1089 .returnValue(ReturnValue: TmpVar)
1090 .finalize();
1091}
1092
1093// Adds static method that initializes resource from binding:
1094//
1095// static Resource<T>
1096// __createFromImplicitBindingWithImplicitCounter(unsigned orderId,
1097// unsigned spaceNo, int range,
1098// unsigned index,
1099// const char *name,
1100// unsigned counterOrderId) {
1101// Resource<T> tmp;
1102// tmp.__handle = __builtin_hlsl_resource_handlefromimplicitbinding(
1103// tmp.__handle, orderId, spaceNo, range, index, name);
1104// tmp.__counter_handle =
1105// __builtin_hlsl_resource_counterhandlefromimplicitbinding(
1106// tmp.__handle, counterOrderId, spaceNo);
1107// return tmp;
1108// }
1109BuiltinTypeDeclBuilder &
1110BuiltinTypeDeclBuilder::addCreateFromImplicitBindingWithImplicitCounter() {
1111 assert(!Record->isCompleteDefinition() && "record is already complete");
1112
1113 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1114 ASTContext &AST = SemaRef.getASTContext();
1115 QualType HandleType = getResourceHandleField()->getType();
1116 QualType CounterHandleType = getResourceCounterHandleField()->getType();
1117 QualType RecordType = AST.getTypeDeclType(Decl: cast<TypeDecl>(Val: Record));
1118 BuiltinTypeMethodBuilder::LocalVar TmpVar("tmp", RecordType);
1119
1120 return BuiltinTypeMethodBuilder(
1121 *this, "__createFromImplicitBindingWithImplicitCounter",
1122 RecordType, false, false, SC_Static)
1123 .addParam(Name: "orderId", Ty: AST.UnsignedIntTy)
1124 .addParam(Name: "spaceNo", Ty: AST.UnsignedIntTy)
1125 .addParam(Name: "range", Ty: AST.IntTy)
1126 .addParam(Name: "index", Ty: AST.UnsignedIntTy)
1127 .addParam(Name: "name", Ty: AST.getPointerType(T: AST.CharTy.withConst()))
1128 .addParam(Name: "counterOrderId", Ty: AST.UnsignedIntTy)
1129 .declareLocalVar(Var&: TmpVar)
1130 .accessHandleFieldOnResource(ResourceRecord: TmpVar)
1131 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_handlefromimplicitbinding",
1132 ReturnType: HandleType, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_0, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3,
1133 ArgSpecs: PH::_4)
1134 .setHandleFieldOnResource(ResourceRecord: TmpVar, HandleValue: PH::LastStmt)
1135 .accessHandleFieldOnResource(ResourceRecord: TmpVar)
1136 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_counterhandlefromimplicitbinding",
1137 ReturnType: CounterHandleType, ArgSpecs: PH::LastStmt, ArgSpecs: PH::_5, ArgSpecs: PH::_1)
1138 .setCounterHandleFieldOnResource(ResourceRecord: TmpVar, HandleValue: PH::LastStmt)
1139 .returnValue(ReturnValue: TmpVar)
1140 .finalize();
1141}
1142
1143BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyConstructor() {
1144 assert(!Record->isCompleteDefinition() && "record is already complete");
1145
1146 ASTContext &AST = SemaRef.getASTContext();
1147 QualType RecordType = AST.getCanonicalTagType(TD: Record);
1148 QualType ConstRecordType = RecordType.withConst();
1149 QualType ConstRecordRefType = AST.getLValueReferenceType(T: ConstRecordType);
1150
1151 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1152
1153 BuiltinTypeMethodBuilder MMB(*this, /*Name=*/"", AST.VoidTy,
1154 /*IsConst=*/false, /*IsCtor=*/true);
1155 MMB.addParam(Name: "other", Ty: ConstRecordRefType)
1156 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
1157 .assign(LHS: PH::Handle, RHS: PH::LastStmt);
1158
1159 if (getResourceCounterHandleField())
1160 MMB.accessCounterHandleFieldOnResource(ResourceRecord: PH::_0).assign(LHS: PH::CounterHandle,
1161 RHS: PH::LastStmt);
1162
1163 return MMB.finalize();
1164}
1165
1166BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyAssignmentOperator() {
1167 assert(!Record->isCompleteDefinition() && "record is already complete");
1168
1169 ASTContext &AST = SemaRef.getASTContext();
1170 QualType RecordType = AST.getCanonicalTagType(TD: Record);
1171 QualType ConstRecordType = RecordType.withConst();
1172 QualType ConstRecordRefType = AST.getLValueReferenceType(T: ConstRecordType);
1173 QualType RecordRefType = AST.getLValueReferenceType(T: RecordType);
1174
1175 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1176 DeclarationName Name = AST.DeclarationNames.getCXXOperatorName(Op: OO_Equal);
1177 BuiltinTypeMethodBuilder MMB(*this, Name, RecordRefType);
1178 MMB.addParam(Name: "other", Ty: ConstRecordRefType)
1179 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
1180 .assign(LHS: PH::Handle, RHS: PH::LastStmt);
1181
1182 if (getResourceCounterHandleField())
1183 MMB.accessCounterHandleFieldOnResource(ResourceRecord: PH::_0).assign(LHS: PH::CounterHandle,
1184 RHS: PH::LastStmt);
1185
1186 return MMB.returnThis().finalize();
1187}
1188
1189BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() {
1190 ASTContext &AST = Record->getASTContext();
1191 DeclarationName Subscript =
1192 AST.DeclarationNames.getCXXOperatorName(Op: OO_Subscript);
1193
1194 addHandleAccessFunction(Name&: Subscript, /*IsConst=*/true, /*IsRef=*/true);
1195 if (getResourceAttrs().ResourceClass == llvm::dxil::ResourceClass::UAV)
1196 addHandleAccessFunction(Name&: Subscript, /*IsConst=*/false, /*IsRef=*/true);
1197
1198 return *this;
1199}
1200
1201BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addLoadMethods() {
1202 assert(!Record->isCompleteDefinition() && "record is already complete");
1203
1204 ASTContext &AST = Record->getASTContext();
1205 IdentifierInfo &II = AST.Idents.get(Name: "Load", TokenCode: tok::TokenKind::identifier);
1206 DeclarationName Load(&II);
1207 // TODO: We also need versions with status for CheckAccessFullyMapped.
1208 addHandleAccessFunction(Name&: Load, /*IsConst=*/false, /*IsRef=*/false);
1209 addLoadWithStatusFunction(Name&: Load, /*IsConst=*/false);
1210
1211 return *this;
1212}
1213
1214BuiltinTypeDeclBuilder &
1215BuiltinTypeDeclBuilder::addSampleMethods(ResourceDimension Dim) {
1216 assert(!Record->isCompleteDefinition() && "record is already complete");
1217
1218 ASTContext &AST = Record->getASTContext();
1219 QualType ReturnType = getFirstTemplateTypeParam();
1220
1221 QualType SamplerStateType =
1222 lookupBuiltinType(S&: SemaRef, Name: "SamplerState", DC: Record->getDeclContext());
1223
1224 uint32_t VecSize = getResourceDimensions(Dim);
1225
1226 QualType FloatTy = AST.FloatTy;
1227 QualType Float2Ty = AST.getExtVectorType(VectorType: FloatTy, NumElts: VecSize);
1228
1229 QualType IntTy = AST.IntTy;
1230 QualType Int2Ty = AST.getExtVectorType(VectorType: IntTy, NumElts: VecSize);
1231
1232 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1233
1234 // T Sample(SamplerState s, float2 location)
1235 BuiltinTypeMethodBuilder(*this, "Sample", ReturnType)
1236 .addParam(Name: "Sampler", Ty: SamplerStateType)
1237 .addParam(Name: "Location", Ty: Float2Ty)
1238 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
1239 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample", ReturnType, ArgSpecs: PH::Handle,
1240 ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1)
1241 .returnValue(ReturnValue: PH::LastStmt)
1242 .finalize();
1243
1244 // T Sample(SamplerState s, float2 location, int2 offset)
1245 BuiltinTypeMethodBuilder(*this, "Sample", ReturnType)
1246 .addParam(Name: "Sampler", Ty: SamplerStateType)
1247 .addParam(Name: "Location", Ty: Float2Ty)
1248 .addParam(Name: "Offset", Ty: Int2Ty)
1249 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
1250 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample", ReturnType, ArgSpecs: PH::Handle,
1251 ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2)
1252 .returnValue(ReturnValue: PH::LastStmt)
1253 .finalize();
1254
1255 // T Sample(SamplerState s, float2 location, int2 offset, float clamp)
1256 return BuiltinTypeMethodBuilder(*this, "Sample", ReturnType)
1257 .addParam(Name: "Sampler", Ty: SamplerStateType)
1258 .addParam(Name: "Location", Ty: Float2Ty)
1259 .addParam(Name: "Offset", Ty: Int2Ty)
1260 .addParam(Name: "Clamp", Ty: FloatTy)
1261 .accessHandleFieldOnResource(ResourceRecord: PH::_0)
1262 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_sample", ReturnType, ArgSpecs: PH::Handle,
1263 ArgSpecs: PH::LastStmt, ArgSpecs: PH::_1, ArgSpecs: PH::_2, ArgSpecs: PH::_3)
1264 .returnValue(ReturnValue: PH::LastStmt)
1265 .finalize();
1266}
1267
1268FieldDecl *BuiltinTypeDeclBuilder::getResourceHandleField() const {
1269 auto I = Fields.find(Key: "__handle");
1270 assert(I != Fields.end() &&
1271 I->second->getType()->isHLSLAttributedResourceType() &&
1272 "record does not have resource handle field");
1273 return I->second;
1274}
1275
1276FieldDecl *BuiltinTypeDeclBuilder::getResourceCounterHandleField() const {
1277 auto I = Fields.find(Key: "__counter_handle");
1278 if (I == Fields.end() ||
1279 !I->second->getType()->isHLSLAttributedResourceType())
1280 return nullptr;
1281 return I->second;
1282}
1283
1284QualType BuiltinTypeDeclBuilder::getFirstTemplateTypeParam() {
1285 assert(Template && "record it not a template");
1286 if (const auto *TTD = dyn_cast<TemplateTypeParmDecl>(
1287 Val: Template->getTemplateParameters()->getParam(Idx: 0))) {
1288 return QualType(TTD->getTypeForDecl(), 0);
1289 }
1290 return QualType();
1291}
1292
1293QualType BuiltinTypeDeclBuilder::getHandleElementType() {
1294 if (Template)
1295 return getFirstTemplateTypeParam();
1296 // TODO: Should we default to VoidTy? Using `i8` is arguably ambiguous.
1297 return SemaRef.getASTContext().Char8Ty;
1298}
1299
1300HLSLAttributedResourceType::Attributes
1301BuiltinTypeDeclBuilder::getResourceAttrs() const {
1302 QualType HandleType = getResourceHandleField()->getType();
1303 return cast<HLSLAttributedResourceType>(Val&: HandleType)->getAttrs();
1304}
1305
1306BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::completeDefinition() {
1307 assert(!Record->isCompleteDefinition() && "record is already complete");
1308 assert(Record->isBeingDefined() &&
1309 "Definition must be started before completing it.");
1310
1311 Record->completeDefinition();
1312 return *this;
1313}
1314
1315Expr *BuiltinTypeDeclBuilder::getConstantIntExpr(int value) {
1316 ASTContext &AST = SemaRef.getASTContext();
1317 return IntegerLiteral::Create(
1318 C: AST, V: llvm::APInt(AST.getTypeSize(T: AST.IntTy), value, true), type: AST.IntTy,
1319 l: SourceLocation());
1320}
1321
1322BuiltinTypeDeclBuilder &
1323BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
1324 ConceptDecl *CD = nullptr) {
1325 if (Record->isCompleteDefinition()) {
1326 assert(Template && "existing record it not a template");
1327 assert(Template->getTemplateParameters()->size() == Names.size() &&
1328 "template param count mismatch");
1329 return *this;
1330 }
1331
1332 TemplateParameterListBuilder Builder = TemplateParameterListBuilder(*this);
1333 for (StringRef Name : Names)
1334 Builder.addTypeParameter(Name);
1335 return Builder.finalizeTemplateArgs(CD);
1336}
1337
1338BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() {
1339 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1340 QualType UnsignedIntTy = SemaRef.getASTContext().UnsignedIntTy;
1341 return BuiltinTypeMethodBuilder(*this, "IncrementCounter", UnsignedIntTy)
1342 .callBuiltin(BuiltinName: "__builtin_hlsl_buffer_update_counter", ReturnType: UnsignedIntTy,
1343 ArgSpecs: PH::CounterHandle, ArgSpecs: getConstantIntExpr(value: 1))
1344 .finalize();
1345}
1346
1347BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
1348 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1349 QualType UnsignedIntTy = SemaRef.getASTContext().UnsignedIntTy;
1350 return BuiltinTypeMethodBuilder(*this, "DecrementCounter", UnsignedIntTy)
1351 .callBuiltin(BuiltinName: "__builtin_hlsl_buffer_update_counter", ReturnType: UnsignedIntTy,
1352 ArgSpecs: PH::CounterHandle, ArgSpecs: getConstantIntExpr(value: -1))
1353 .finalize();
1354}
1355
1356BuiltinTypeDeclBuilder &
1357BuiltinTypeDeclBuilder::addLoadWithStatusFunction(DeclarationName &Name,
1358 bool IsConst) {
1359 assert(!Record->isCompleteDefinition() && "record is already complete");
1360 ASTContext &AST = SemaRef.getASTContext();
1361 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1362
1363 QualType ReturnTy = getHandleElementType();
1364 return BuiltinTypeMethodBuilder(*this, Name, ReturnTy, IsConst)
1365 .addParam(Name: "Index", Ty: AST.UnsignedIntTy)
1366 .addParam(Name: "Status", Ty: AST.UnsignedIntTy, Modifier: HLSLParamModifierAttr::Keyword_out)
1367 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_load_with_status", ReturnType: ReturnTy,
1368 ArgSpecs: PH::Handle, ArgSpecs: PH::_0, ArgSpecs: PH::_1)
1369 .finalize();
1370}
1371
1372BuiltinTypeDeclBuilder &
1373BuiltinTypeDeclBuilder::addHandleAccessFunction(DeclarationName &Name,
1374 bool IsConst, bool IsRef) {
1375 assert(!Record->isCompleteDefinition() && "record is already complete");
1376 ASTContext &AST = SemaRef.getASTContext();
1377 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1378
1379 QualType ElemTy = getHandleElementType();
1380 QualType AddrSpaceElemTy =
1381 AST.getAddrSpaceQualType(T: ElemTy, AddressSpace: LangAS::hlsl_device);
1382 QualType ElemPtrTy = AST.getPointerType(T: AddrSpaceElemTy);
1383 QualType ReturnTy;
1384
1385 if (IsRef) {
1386 ReturnTy = AddrSpaceElemTy;
1387 if (IsConst)
1388 ReturnTy.addConst();
1389 ReturnTy = AST.getLValueReferenceType(T: ReturnTy);
1390 } else {
1391 ReturnTy = ElemTy;
1392 if (IsConst)
1393 ReturnTy.addConst();
1394 }
1395
1396 return BuiltinTypeMethodBuilder(*this, Name, ReturnTy, IsConst)
1397 .addParam(Name: "Index", Ty: AST.UnsignedIntTy)
1398 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_getpointer", ReturnType: ElemPtrTy, ArgSpecs: PH::Handle,
1399 ArgSpecs: PH::_0)
1400 .dereference(Ptr: PH::LastStmt)
1401 .finalize();
1402}
1403
1404BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() {
1405 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1406 ASTContext &AST = SemaRef.getASTContext();
1407 QualType ElemTy = getHandleElementType();
1408 QualType AddrSpaceElemTy =
1409 AST.getAddrSpaceQualType(T: ElemTy, AddressSpace: LangAS::hlsl_device);
1410 return BuiltinTypeMethodBuilder(*this, "Append", AST.VoidTy)
1411 .addParam(Name: "value", Ty: ElemTy)
1412 .callBuiltin(BuiltinName: "__builtin_hlsl_buffer_update_counter", ReturnType: AST.UnsignedIntTy,
1413 ArgSpecs: PH::CounterHandle, ArgSpecs: getConstantIntExpr(value: 1))
1414 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_getpointer",
1415 ReturnType: AST.getPointerType(T: AddrSpaceElemTy), ArgSpecs: PH::Handle,
1416 ArgSpecs: PH::LastStmt)
1417 .dereference(Ptr: PH::LastStmt)
1418 .assign(LHS: PH::LastStmt, RHS: PH::_0)
1419 .finalize();
1420}
1421
1422BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() {
1423 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1424 ASTContext &AST = SemaRef.getASTContext();
1425 QualType ElemTy = getHandleElementType();
1426 QualType AddrSpaceElemTy =
1427 AST.getAddrSpaceQualType(T: ElemTy, AddressSpace: LangAS::hlsl_device);
1428 return BuiltinTypeMethodBuilder(*this, "Consume", ElemTy)
1429 .callBuiltin(BuiltinName: "__builtin_hlsl_buffer_update_counter", ReturnType: AST.UnsignedIntTy,
1430 ArgSpecs: PH::CounterHandle, ArgSpecs: getConstantIntExpr(value: -1))
1431 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_getpointer",
1432 ReturnType: AST.getPointerType(T: AddrSpaceElemTy), ArgSpecs: PH::Handle,
1433 ArgSpecs: PH::LastStmt)
1434 .dereference(Ptr: PH::LastStmt)
1435 .finalize();
1436}
1437
1438BuiltinTypeDeclBuilder &
1439BuiltinTypeDeclBuilder::addGetDimensionsMethodForBuffer() {
1440 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1441 ASTContext &AST = SemaRef.getASTContext();
1442 QualType UIntTy = AST.UnsignedIntTy;
1443
1444 QualType HandleTy = getResourceHandleField()->getType();
1445 auto *AttrResTy = cast<HLSLAttributedResourceType>(Val: HandleTy.getTypePtr());
1446
1447 // Structured buffers except {RW}ByteAddressBuffer have overload
1448 // GetDimensions(out uint numStructs, out uint stride).
1449 if (AttrResTy->getAttrs().RawBuffer &&
1450 AttrResTy->getContainedType() != AST.Char8Ty) {
1451 return BuiltinTypeMethodBuilder(*this, "GetDimensions", AST.VoidTy)
1452 .addParam(Name: "numStructs", Ty: UIntTy, Modifier: HLSLParamModifierAttr::Keyword_out)
1453 .addParam(Name: "stride", Ty: UIntTy, Modifier: HLSLParamModifierAttr::Keyword_out)
1454 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_getdimensions_x", ReturnType: QualType(),
1455 ArgSpecs: PH::Handle, ArgSpecs: PH::_0)
1456 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_getstride", ReturnType: QualType(),
1457 ArgSpecs: PH::Handle, ArgSpecs: PH::_1)
1458 .finalize();
1459 }
1460
1461 // Typed buffers and {RW}ByteAddressBuffer have overload
1462 // GetDimensions(out uint dim).
1463 return BuiltinTypeMethodBuilder(*this, "GetDimensions", AST.VoidTy)
1464 .addParam(Name: "dim", Ty: UIntTy, Modifier: HLSLParamModifierAttr::Keyword_out)
1465 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_getdimensions_x", ReturnType: QualType(),
1466 ArgSpecs: PH::Handle, ArgSpecs: PH::_0)
1467 .finalize();
1468}
1469
1470} // namespace hlsl
1471} // namespace clang
1472