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/Type.h"
21#include "clang/Basic/SourceLocation.h"
22#include "clang/Sema/Lookup.h"
23#include "clang/Sema/Sema.h"
24#include "clang/Sema/SemaHLSL.h"
25#include "llvm/ADT/SmallVector.h"
26
27using namespace llvm::hlsl;
28
29namespace clang {
30
31namespace hlsl {
32
33namespace {
34
35static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name) {
36 IdentifierInfo &II =
37 S.getASTContext().Idents.get(Name, TokenCode: tok::TokenKind::identifier);
38 DeclarationNameInfo NameInfo =
39 DeclarationNameInfo(DeclarationName(&II), SourceLocation());
40 LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
41 // AllowBuiltinCreation is false but LookupDirect will create
42 // the builtin when searching the global scope anyways...
43 S.LookupName(R, S: S.getCurScope());
44 // FIXME: If the builtin function was user-declared in global scope,
45 // this assert *will* fail. Should this call LookupBuiltin instead?
46 assert(R.isSingleResult() &&
47 "Since this is a builtin it should always resolve!");
48 return cast<FunctionDecl>(Val: R.getFoundDecl());
49}
50} // namespace
51
52// Builder for template arguments of builtin types. Used internally
53// by BuiltinTypeDeclBuilder.
54struct TemplateParameterListBuilder {
55 BuiltinTypeDeclBuilder &Builder;
56 llvm::SmallVector<NamedDecl *> Params;
57
58 TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB) : Builder(RB) {}
59 ~TemplateParameterListBuilder();
60
61 TemplateParameterListBuilder &
62 addTypeParameter(StringRef Name, QualType DefaultValue = QualType());
63
64 ConceptSpecializationExpr *
65 constructConceptSpecializationExpr(Sema &S, ConceptDecl *CD);
66
67 BuiltinTypeDeclBuilder &finalizeTemplateArgs(ConceptDecl *CD = nullptr);
68};
69
70// Builder for methods or constructors of builtin types. Allows creating methods
71// or constructors of builtin types using the builder pattern like this:
72//
73// BuiltinTypeMethodBuilder(RecordBuilder, "MethodName", ReturnType)
74// .addParam("param_name", Type, InOutModifier)
75// .callBuiltin("builtin_name", BuiltinParams...)
76// .finalize();
77//
78// The builder needs to have all of the parameters before it can create
79// a CXXMethodDecl or CXXConstructorDecl. It collects them in addParam calls and
80// when a first method that builds the body is called or when access to 'this`
81// is needed it creates the CXXMethodDecl/CXXConstructorDecl and ParmVarDecls
82// instances. These can then be referenced from the body building methods.
83// Destructor or an explicit call to finalize() will complete the method
84// definition.
85//
86// The callBuiltin helper method accepts constants via `Expr *` or placeholder
87// value arguments to indicate which function arguments to forward to the
88// builtin.
89//
90// If the method that is being built has a non-void return type the
91// finalize() will create a return statement with the value of the last
92// statement (unless the last statement is already a ReturnStmt or the return
93// value is void).
94struct BuiltinTypeMethodBuilder {
95private:
96 struct Param {
97 const IdentifierInfo &NameII;
98 QualType Ty;
99 HLSLParamModifierAttr::Spelling Modifier;
100 Param(const IdentifierInfo &NameII, QualType Ty,
101 HLSLParamModifierAttr::Spelling Modifier)
102 : NameII(NameII), Ty(Ty), Modifier(Modifier) {}
103 };
104
105 BuiltinTypeDeclBuilder &DeclBuilder;
106 DeclarationName Name;
107 QualType ReturnTy;
108 // method or constructor declaration
109 // (CXXConstructorDecl derives from CXXMethodDecl)
110 CXXMethodDecl *Method;
111 bool IsConst;
112 bool IsCtor;
113 llvm::SmallVector<Param> Params;
114 llvm::SmallVector<Stmt *> StmtsList;
115
116 // Argument placeholders, inspired by std::placeholder. These are the indices
117 // of arguments to forward to `callBuiltin` and other method builder methods.
118 // Additional special values are:
119 // Handle - refers to the resource handle.
120 // LastStmt - refers to the last statement in the method body; referencing
121 // LastStmt will remove the statement from the method body since
122 // it will be linked from the new expression being constructed.
123 enum class PlaceHolder { _0, _1, _2, _3, _4, Handle = 128, LastStmt };
124
125 Expr *convertPlaceholder(PlaceHolder PH);
126 Expr *convertPlaceholder(Expr *E) { return E; }
127
128public:
129 friend BuiltinTypeDeclBuilder;
130
131 BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, DeclarationName &Name,
132 QualType ReturnTy, bool IsConst = false,
133 bool IsCtor = false)
134 : DeclBuilder(DB), Name(Name), ReturnTy(ReturnTy), Method(nullptr),
135 IsConst(IsConst), IsCtor(IsCtor) {}
136
137 BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, StringRef NameStr,
138 QualType ReturnTy, bool IsConst = false,
139 bool IsCtor = false);
140 BuiltinTypeMethodBuilder(const BuiltinTypeMethodBuilder &Other) = delete;
141
142 ~BuiltinTypeMethodBuilder() { finalize(); }
143
144 BuiltinTypeMethodBuilder &
145 operator=(const BuiltinTypeMethodBuilder &Other) = delete;
146
147 BuiltinTypeMethodBuilder &addParam(StringRef Name, QualType Ty,
148 HLSLParamModifierAttr::Spelling Modifier =
149 HLSLParamModifierAttr::Keyword_in);
150 template <typename... Ts>
151 BuiltinTypeMethodBuilder &callBuiltin(StringRef BuiltinName,
152 QualType ReturnType, Ts... ArgSpecs);
153 template <typename TLHS, typename TRHS>
154 BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS);
155 template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr);
156 BuiltinTypeDeclBuilder &finalize();
157 Expr *getResourceHandleExpr();
158
159private:
160 void createDecl();
161
162 // Makes sure the declaration is created; should be called before any
163 // statement added to the body or when access to 'this' is needed.
164 void ensureCompleteDecl() {
165 if (!Method)
166 createDecl();
167 }
168};
169
170TemplateParameterListBuilder::~TemplateParameterListBuilder() {
171 finalizeTemplateArgs();
172}
173
174TemplateParameterListBuilder &
175TemplateParameterListBuilder::addTypeParameter(StringRef Name,
176 QualType DefaultValue) {
177 assert(!Builder.Record->isCompleteDefinition() &&
178 "record is already complete");
179 ASTContext &AST = Builder.SemaRef.getASTContext();
180 unsigned Position = static_cast<unsigned>(Params.size());
181 auto *Decl = TemplateTypeParmDecl::Create(
182 C: AST, DC: Builder.Record->getDeclContext(), KeyLoc: SourceLocation(), NameLoc: SourceLocation(),
183 /* TemplateDepth */ D: 0, P: Position,
184 Id: &AST.Idents.get(Name, TokenCode: tok::TokenKind::identifier),
185 /* Typename */ true,
186 /* ParameterPack */ false,
187 /* HasTypeConstraint*/ false);
188 if (!DefaultValue.isNull())
189 Decl->setDefaultArgument(AST,
190 Builder.SemaRef.getTrivialTemplateArgumentLoc(
191 DefaultValue, QualType(), SourceLocation()));
192
193 Params.emplace_back(Decl);
194 return *this;
195}
196
197// The concept specialization expression (CSE) constructed in
198// constructConceptSpecializationExpr is constructed so that it
199// matches the CSE that is constructed when parsing the below C++ code:
200//
201// template<typename T>
202// concept is_typed_resource_element_compatible =
203// __builtin_hlsl_typed_resource_element_compatible<T>
204//
205// template<typename element_type> requires
206// is_typed_resource_element_compatible<element_type>
207// struct RWBuffer {
208// element_type Val;
209// };
210//
211// int fn() {
212// RWBuffer<int> Buf;
213// }
214//
215// When dumping the AST and filtering for "RWBuffer", the resulting AST
216// structure is what we're trying to construct below, specifically the
217// CSE portion.
218ConceptSpecializationExpr *
219TemplateParameterListBuilder::constructConceptSpecializationExpr(
220 Sema &S, ConceptDecl *CD) {
221 ASTContext &Context = S.getASTContext();
222 SourceLocation Loc = Builder.Record->getBeginLoc();
223 DeclarationNameInfo DNI(CD->getDeclName(), Loc);
224 NestedNameSpecifierLoc NNSLoc;
225 DeclContext *DC = Builder.Record->getDeclContext();
226 TemplateArgumentListInfo TALI(Loc, Loc);
227
228 // Assume that the concept decl has just one template parameter
229 // This parameter should have been added when CD was constructed
230 // in getTypedBufferConceptDecl
231 assert(CD->getTemplateParameters()->size() == 1 &&
232 "unexpected concept decl parameter count");
233 TemplateTypeParmDecl *ConceptTTPD =
234 dyn_cast<TemplateTypeParmDecl>(CD->getTemplateParameters()->getParam(0));
235
236 // this TemplateTypeParmDecl is the template for the resource, and is
237 // used to construct a template argumentthat will be used
238 // to construct the ImplicitConceptSpecializationDecl
239 TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create(
240 C: Context, // AST context
241 DC: Builder.Record->getDeclContext(), // DeclContext
242 KeyLoc: SourceLocation(), NameLoc: SourceLocation(),
243 /*D=*/0, // Depth in the template parameter list
244 /*P=*/0, // Position in the template parameter list
245 /*Id=*/nullptr, // Identifier for 'T'
246 /*Typename=*/true, // Indicates this is a 'typename' or 'class'
247 /*ParameterPack=*/false, // Not a parameter pack
248 /*HasTypeConstraint=*/false // Has no type constraint
249 );
250
251 T->setDeclContext(DC);
252
253 QualType ConceptTType = Context.getTypeDeclType(ConceptTTPD);
254
255 // this is the 2nd template argument node, on which
256 // the concept constraint is actually being applied: 'element_type'
257 TemplateArgument ConceptTA = TemplateArgument(ConceptTType);
258
259 QualType CSETType = Context.getTypeDeclType(T);
260
261 // this is the 1st template argument node, which represents
262 // the abstract type that a concept would refer to: 'T'
263 TemplateArgument CSETA = TemplateArgument(CSETType);
264
265 ImplicitConceptSpecializationDecl *ImplicitCSEDecl =
266 ImplicitConceptSpecializationDecl::Create(
267 C: Context, DC: Builder.Record->getDeclContext(), SL: Loc, ConvertedArgs: {CSETA});
268
269 // Constraint satisfaction is used to construct the
270 // ConceptSpecailizationExpr, and represents the 2nd Template Argument,
271 // located at the bottom of the sample AST above.
272 const ConstraintSatisfaction CS(CD, {ConceptTA});
273 TemplateArgumentLoc TAL =
274 S.getTrivialTemplateArgumentLoc(Arg: ConceptTA, NTTPType: QualType(), Loc: SourceLocation());
275
276 TALI.addArgument(Loc: TAL);
277 const ASTTemplateArgumentListInfo *ATALI =
278 ASTTemplateArgumentListInfo::Create(C: Context, List: TALI);
279
280 // In the concept reference, ATALI is what adds the extra
281 // TemplateArgument node underneath CSE
282 ConceptReference *CR =
283 ConceptReference::Create(Context, NNSLoc, Loc, DNI, CD, CD, ATALI);
284
285 ConceptSpecializationExpr *CSE =
286 ConceptSpecializationExpr::Create(C: Context, ConceptRef: CR, SpecDecl: ImplicitCSEDecl, Satisfaction: &CS);
287
288 return CSE;
289}
290
291BuiltinTypeDeclBuilder &
292TemplateParameterListBuilder::finalizeTemplateArgs(ConceptDecl *CD) {
293 if (Params.empty())
294 return Builder;
295
296 ASTContext &AST = Builder.SemaRef.Context;
297 ConceptSpecializationExpr *CSE =
298 CD ? constructConceptSpecializationExpr(S&: Builder.SemaRef, CD) : nullptr;
299 auto *ParamList = TemplateParameterList::Create(
300 AST, SourceLocation(), SourceLocation(), Params, SourceLocation(), CSE);
301 Builder.Template = ClassTemplateDecl::Create(
302 C&: AST, DC: Builder.Record->getDeclContext(), L: SourceLocation(),
303 Name: DeclarationName(Builder.Record->getIdentifier()), Params: ParamList,
304 Decl: Builder.Record);
305
306 Builder.Record->setDescribedClassTemplate(Builder.Template);
307 Builder.Template->setImplicit(true);
308 Builder.Template->setLexicalDeclContext(Builder.Record->getDeclContext());
309
310 // NOTE: setPreviousDecl before addDecl so new decl replace old decl when
311 // make visible.
312 Builder.Template->setPreviousDecl(Builder.PrevTemplate);
313 Builder.Record->getDeclContext()->addDecl(Builder.Template);
314 Params.clear();
315
316 QualType T = Builder.Template->getInjectedClassNameSpecialization();
317 T = AST.getInjectedClassNameType(Decl: Builder.Record, TST: T);
318
319 return Builder;
320}
321
322Expr *BuiltinTypeMethodBuilder::convertPlaceholder(PlaceHolder PH) {
323 if (PH == PlaceHolder::Handle)
324 return getResourceHandleExpr();
325
326 if (PH == PlaceHolder::LastStmt) {
327 assert(!StmtsList.empty() && "no statements in the list");
328 Stmt *LastStmt = StmtsList.pop_back_val();
329 assert(isa<ValueStmt>(LastStmt) && "last statement does not have a value");
330 return cast<ValueStmt>(Val: LastStmt)->getExprStmt();
331 }
332
333 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
334 ParmVarDecl *ParamDecl = Method->getParamDecl(static_cast<unsigned>(PH));
335 return DeclRefExpr::Create(
336 AST, NestedNameSpecifierLoc(), SourceLocation(), ParamDecl, false,
337 DeclarationNameInfo(ParamDecl->getDeclName(), SourceLocation()),
338 ParamDecl->getType(), VK_PRValue);
339}
340
341BuiltinTypeMethodBuilder::BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB,
342 StringRef NameStr,
343 QualType ReturnTy,
344 bool IsConst, bool IsCtor)
345 : DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst),
346 IsCtor(IsCtor) {
347
348 assert((!NameStr.empty() || IsCtor) && "method needs a name");
349 assert(((IsCtor && !IsConst) || !IsCtor) && "constructor cannot be const");
350
351 ASTContext &AST = DB.SemaRef.getASTContext();
352 if (IsCtor) {
353 Name = AST.DeclarationNames.getCXXConstructorName(
354 Ty: DB.Record->getTypeForDecl()->getCanonicalTypeUnqualified());
355 } else {
356 const IdentifierInfo &II =
357 AST.Idents.get(Name: NameStr, TokenCode: tok::TokenKind::identifier);
358 Name = DeclarationName(&II);
359 }
360}
361
362BuiltinTypeMethodBuilder &
363BuiltinTypeMethodBuilder::addParam(StringRef Name, QualType Ty,
364 HLSLParamModifierAttr::Spelling Modifier) {
365 assert(Method == nullptr && "Cannot add param, method already created");
366 const IdentifierInfo &II = DeclBuilder.SemaRef.getASTContext().Idents.get(
367 Name, TokenCode: tok::TokenKind::identifier);
368 Params.emplace_back(II, Ty, Modifier);
369 return *this;
370}
371
372void BuiltinTypeMethodBuilder::createDecl() {
373 assert(Method == nullptr && "Method or constructor is already created");
374
375 // create method or constructor type
376 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
377 SmallVector<QualType> ParamTypes;
378 for (Param &MP : Params)
379 ParamTypes.emplace_back(MP.Ty);
380
381 FunctionProtoType::ExtProtoInfo ExtInfo;
382 if (IsConst)
383 ExtInfo.TypeQuals.addConst();
384
385 QualType FuncTy = AST.getFunctionType(ReturnTy, ParamTypes, ExtInfo);
386
387 // create method or constructor decl
388 auto *TSInfo = AST.getTrivialTypeSourceInfo(T: FuncTy, Loc: SourceLocation());
389 DeclarationNameInfo NameInfo = DeclarationNameInfo(Name, SourceLocation());
390 if (IsCtor)
391 Method = CXXConstructorDecl::Create(
392 C&: AST, RD: DeclBuilder.Record, StartLoc: SourceLocation(), NameInfo, T: FuncTy, TInfo: TSInfo,
393 ES: ExplicitSpecifier(), UsesFPIntrin: false, isInline: true, isImplicitlyDeclared: false,
394 ConstexprKind: ConstexprSpecKind::Unspecified);
395 else
396 Method =
397 CXXMethodDecl::Create(C&: AST, RD: DeclBuilder.Record, StartLoc: SourceLocation(),
398 NameInfo, T: FuncTy, TInfo: TSInfo, SC: SC_None, UsesFPIntrin: false, isInline: false,
399 ConstexprKind: ConstexprSpecKind::Unspecified, EndLocation: SourceLocation());
400
401 // create params & set them to the function prototype
402 SmallVector<ParmVarDecl *> ParmDecls;
403 unsigned CurScopeDepth = DeclBuilder.SemaRef.getCurScope()->getDepth();
404 auto FnProtoLoc =
405 Method->getTypeSourceInfo()->getTypeLoc().getAs<FunctionProtoTypeLoc>();
406 for (int I = 0, E = Params.size(); I != E; I++) {
407 Param &MP = Params[I];
408 ParmVarDecl *Parm = ParmVarDecl::Create(
409 C&: AST, DC: Method->getDeclContext(), StartLoc: SourceLocation(), IdLoc: SourceLocation(),
410 Id: &MP.NameII, T: MP.Ty,
411 TInfo: AST.getTrivialTypeSourceInfo(T: MP.Ty, Loc: SourceLocation()), S: SC_None,
412 DefArg: nullptr);
413 if (MP.Modifier != HLSLParamModifierAttr::Keyword_in) {
414 auto *Mod =
415 HLSLParamModifierAttr::Create(AST, SourceRange(), MP.Modifier);
416 Parm->addAttr(A: Mod);
417 }
418 Parm->setScopeInfo(scopeDepth: CurScopeDepth, parameterIndex: I);
419 ParmDecls.push_back(Elt: Parm);
420 FnProtoLoc.setParam(I, Parm);
421 }
422 Method->setParams({ParmDecls});
423}
424
425Expr *BuiltinTypeMethodBuilder::getResourceHandleExpr() {
426 ensureCompleteDecl();
427
428 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
429 CXXThisExpr *This = CXXThisExpr::Create(
430 Ctx: AST, L: SourceLocation(), Ty: Method->getFunctionObjectParameterType(), IsImplicit: true);
431 FieldDecl *HandleField = DeclBuilder.getResourceHandleField();
432 return MemberExpr::CreateImplicit(C: AST, Base: This, IsArrow: false, MemberDecl: HandleField,
433 T: HandleField->getType(), VK: VK_LValue,
434 OK: OK_Ordinary);
435}
436
437template <typename... Ts>
438BuiltinTypeMethodBuilder &
439BuiltinTypeMethodBuilder::callBuiltin(StringRef BuiltinName,
440 QualType ReturnType, Ts... ArgSpecs) {
441 std::array<Expr *, sizeof...(ArgSpecs)> Args{
442 convertPlaceholder(std::forward<Ts>(ArgSpecs))...};
443
444 ensureCompleteDecl();
445
446 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
447 FunctionDecl *FD = lookupBuiltinFunction(S&: DeclBuilder.SemaRef, Name: BuiltinName);
448 DeclRefExpr *DRE = DeclRefExpr::Create(
449 AST, NestedNameSpecifierLoc(), SourceLocation(), FD, false,
450 FD->getNameInfo(), AST.BuiltinFnTy, VK_PRValue);
451
452 auto *ImpCast = ImplicitCastExpr::Create(
453 Context: AST, T: AST.getPointerType(FD->getType()), Kind: CK_BuiltinFnToFnPtr, Operand: DRE, BasePath: nullptr,
454 Cat: VK_PRValue, FPO: FPOptionsOverride());
455
456 if (ReturnType.isNull())
457 ReturnType = FD->getReturnType();
458
459 Expr *Call = CallExpr::Create(Ctx: AST, Fn: ImpCast, Args, Ty: ReturnType, VK: VK_PRValue,
460 RParenLoc: SourceLocation(), FPFeatures: FPOptionsOverride());
461 StmtsList.push_back(Call);
462 return *this;
463}
464
465template <typename TLHS, typename TRHS>
466BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::assign(TLHS LHS, TRHS RHS) {
467 Expr *LHSExpr = convertPlaceholder(LHS);
468 Expr *RHSExpr = convertPlaceholder(RHS);
469 Stmt *AssignStmt = BinaryOperator::Create(
470 C: DeclBuilder.SemaRef.getASTContext(), lhs: LHSExpr, rhs: RHSExpr, opc: BO_Assign,
471 ResTy: LHSExpr->getType(), VK: ExprValueKind::VK_PRValue,
472 OK: ExprObjectKind::OK_Ordinary, opLoc: SourceLocation(), FPFeatures: FPOptionsOverride());
473 StmtsList.push_back(Elt: AssignStmt);
474 return *this;
475}
476
477template <typename T>
478BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::dereference(T Ptr) {
479 Expr *PtrExpr = convertPlaceholder(Ptr);
480 Expr *Deref =
481 UnaryOperator::Create(C: DeclBuilder.SemaRef.getASTContext(), input: PtrExpr,
482 opc: UO_Deref, type: PtrExpr->getType()->getPointeeType(),
483 VK: VK_PRValue, OK: OK_Ordinary, l: SourceLocation(),
484 /*CanOverflow=*/false, FPFeatures: FPOptionsOverride());
485 StmtsList.push_back(Deref);
486 return *this;
487}
488
489BuiltinTypeDeclBuilder &BuiltinTypeMethodBuilder::finalize() {
490 assert(!DeclBuilder.Record->isCompleteDefinition() &&
491 "record is already complete");
492
493 ensureCompleteDecl();
494
495 if (!Method->hasBody()) {
496 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
497 assert((ReturnTy == AST.VoidTy || !StmtsList.empty()) &&
498 "nothing to return from non-void method");
499 if (ReturnTy != AST.VoidTy) {
500 if (Expr *LastExpr = dyn_cast<Expr>(Val: StmtsList.back())) {
501 assert(AST.hasSameUnqualifiedType(LastExpr->getType(),
502 ReturnTy.getNonReferenceType()) &&
503 "Return type of the last statement must match the return type "
504 "of the method");
505 if (!isa<ReturnStmt>(Val: LastExpr)) {
506 StmtsList.pop_back();
507 StmtsList.push_back(
508 ReturnStmt::Create(Ctx: AST, RL: SourceLocation(), E: LastExpr, NRVOCandidate: nullptr));
509 }
510 }
511 }
512
513 Method->setBody(CompoundStmt::Create(C: AST, Stmts: StmtsList, FPFeatures: FPOptionsOverride(),
514 LB: SourceLocation(), RB: SourceLocation()));
515 Method->setLexicalDeclContext(DeclBuilder.Record);
516 Method->setAccess(AccessSpecifier::AS_public);
517 Method->addAttr(AlwaysInlineAttr::CreateImplicit(
518 AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline));
519 DeclBuilder.Record->addDecl(Method);
520 }
521 return DeclBuilder;
522}
523
524BuiltinTypeDeclBuilder::BuiltinTypeDeclBuilder(Sema &SemaRef, CXXRecordDecl *R)
525 : SemaRef(SemaRef), Record(R) {
526 Record->startDefinition();
527 Template = Record->getDescribedClassTemplate();
528}
529
530BuiltinTypeDeclBuilder::BuiltinTypeDeclBuilder(Sema &SemaRef,
531 NamespaceDecl *Namespace,
532 StringRef Name)
533 : SemaRef(SemaRef), HLSLNamespace(Namespace) {
534 ASTContext &AST = SemaRef.getASTContext();
535 IdentifierInfo &II = AST.Idents.get(Name, TokenCode: tok::TokenKind::identifier);
536
537 LookupResult Result(SemaRef, &II, SourceLocation(), Sema::LookupTagName);
538 CXXRecordDecl *PrevDecl = nullptr;
539 if (SemaRef.LookupQualifiedName(Result, HLSLNamespace)) {
540 // Declaration already exists (from precompiled headers)
541 NamedDecl *Found = Result.getFoundDecl();
542 if (auto *TD = dyn_cast<ClassTemplateDecl>(Val: Found)) {
543 PrevDecl = TD->getTemplatedDecl();
544 PrevTemplate = TD;
545 } else
546 PrevDecl = dyn_cast<CXXRecordDecl>(Val: Found);
547 assert(PrevDecl && "Unexpected lookup result type.");
548 }
549
550 if (PrevDecl && PrevDecl->isCompleteDefinition()) {
551 Record = PrevDecl;
552 Template = PrevTemplate;
553 return;
554 }
555
556 Record = CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, HLSLNamespace,
557 SourceLocation(), SourceLocation(), &II,
558 PrevDecl, true);
559 Record->setImplicit(true);
560 Record->setLexicalDeclContext(HLSLNamespace);
561 Record->setHasExternalLexicalStorage();
562
563 // Don't let anyone derive from built-in types.
564 Record->addAttr(
565 FinalAttr::CreateImplicit(AST, SourceRange(), FinalAttr::Keyword_final));
566}
567
568BuiltinTypeDeclBuilder::~BuiltinTypeDeclBuilder() {
569 if (HLSLNamespace && !Template && Record->getDeclContext() == HLSLNamespace)
570 HLSLNamespace->addDecl(Record);
571}
572
573CXXRecordDecl *BuiltinTypeDeclBuilder::finalizeForwardDeclaration() {
574 // Force the QualType to be generated for the record declaration. In most
575 // cases this will happen naturally when something uses the type the
576 // QualType gets lazily created. Unfortunately, with our injected types if a
577 // type isn't used in a translation unit the QualType may not get
578 // automatically generated before a PCH is generated. To resolve this we
579 // just force that the QualType is generated after we create a forward
580 // declaration.
581 (void)Record->getASTContext().getRecordType(Record);
582 return Record;
583}
584
585BuiltinTypeDeclBuilder &
586BuiltinTypeDeclBuilder::addMemberVariable(StringRef Name, QualType Type,
587 llvm::ArrayRef<Attr *> Attrs,
588 AccessSpecifier Access) {
589 assert(!Record->isCompleteDefinition() && "record is already complete");
590 assert(Record->isBeingDefined() &&
591 "Definition must be started before adding members!");
592 ASTContext &AST = Record->getASTContext();
593
594 IdentifierInfo &II = AST.Idents.get(Name, TokenCode: tok::TokenKind::identifier);
595 TypeSourceInfo *MemTySource =
596 AST.getTrivialTypeSourceInfo(T: Type, Loc: SourceLocation());
597 auto *Field = FieldDecl::Create(
598 AST, Record, SourceLocation(), SourceLocation(), &II, Type, MemTySource,
599 nullptr, false, InClassInitStyle::ICIS_NoInit);
600 Field->setAccess(Access);
601 Field->setImplicit(true);
602 for (Attr *A : Attrs) {
603 if (A)
604 Field->addAttr(A);
605 }
606
607 Record->addDecl(D: Field);
608 Fields[Name] = Field;
609 return *this;
610}
611
612BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleMember(
613 ResourceClass RC, bool IsROV, bool RawBuffer, AccessSpecifier Access) {
614 assert(!Record->isCompleteDefinition() && "record is already complete");
615
616 ASTContext &Ctx = SemaRef.getASTContext();
617 TypeSourceInfo *ElementTypeInfo =
618 Ctx.getTrivialTypeSourceInfo(T: getHandleElementType(), Loc: SourceLocation());
619
620 // add handle member with resource type attributes
621 QualType AttributedResTy = QualType();
622 SmallVector<const Attr *> Attrs = {
623 HLSLResourceClassAttr::CreateImplicit(Ctx, RC),
624 IsROV ? HLSLROVAttr::CreateImplicit(Ctx) : nullptr,
625 RawBuffer ? HLSLRawBufferAttr::CreateImplicit(Ctx) : nullptr,
626 ElementTypeInfo
627 ? HLSLContainedTypeAttr::CreateImplicit(Ctx, ElementTypeInfo)
628 : nullptr};
629 if (CreateHLSLAttributedResourceType(SemaRef, Ctx.HLSLResourceTy, Attrs,
630 AttributedResTy))
631 addMemberVariable(Name: "__handle", Type: AttributedResTy, Attrs: {}, Access);
632 return *this;
633}
634
635// Adds default constructor to the resource class:
636// Resource::Resource()
637BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDefaultHandleConstructor() {
638 if (Record->isCompleteDefinition())
639 return *this;
640
641 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
642 QualType HandleType = getResourceHandleField()->getType();
643 return BuiltinTypeMethodBuilder(*this, "", SemaRef.getASTContext().VoidTy,
644 false, true)
645 .callBuiltin(BuiltinName: "__builtin_hlsl_resource_uninitializedhandle", ReturnType: HandleType,
646 ArgSpecs: PH::Handle)
647 .assign(LHS: PH::Handle, RHS: PH::LastStmt)
648 .finalize();
649}
650
651BuiltinTypeDeclBuilder &
652BuiltinTypeDeclBuilder::addHandleConstructorFromBinding() {
653 if (Record->isCompleteDefinition())
654 return *this;
655
656 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
657 ASTContext &AST = SemaRef.getASTContext();
658 QualType HandleType = getResourceHandleField()->getType();
659
660 return BuiltinTypeMethodBuilder(*this, "", AST.VoidTy, false, true)
661 .addParam("registerNo", AST.UnsignedIntTy)
662 .addParam("spaceNo", AST.UnsignedIntTy)
663 .addParam("range", AST.IntTy)
664 .addParam("index", AST.UnsignedIntTy)
665 .addParam("name", AST.getPointerType(AST.CharTy.withConst()))
666 .callBuiltin("__builtin_hlsl_resource_handlefrombinding", HandleType,
667 PH::Handle, PH::_0, PH::_1, PH::_2, PH::_3, PH::_4)
668 .assign(PH::Handle, PH::LastStmt)
669 .finalize();
670}
671
672BuiltinTypeDeclBuilder &
673BuiltinTypeDeclBuilder::addHandleConstructorFromImplicitBinding() {
674 if (Record->isCompleteDefinition())
675 return *this;
676
677 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
678 ASTContext &AST = SemaRef.getASTContext();
679 QualType HandleType = getResourceHandleField()->getType();
680
681 return BuiltinTypeMethodBuilder(*this, "", AST.VoidTy, false, true)
682 .addParam("spaceNo", AST.UnsignedIntTy)
683 .addParam("range", AST.IntTy)
684 .addParam("index", AST.UnsignedIntTy)
685 .addParam("orderId", AST.UnsignedIntTy)
686 .addParam("name", AST.getPointerType(AST.CharTy.withConst()))
687 .callBuiltin("__builtin_hlsl_resource_handlefromimplicitbinding",
688 HandleType, PH::Handle, PH::_0, PH::_1, PH::_2, PH::_3,
689 PH::_4)
690 .assign(PH::Handle, PH::LastStmt)
691 .finalize();
692}
693
694BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() {
695 ASTContext &AST = Record->getASTContext();
696 DeclarationName Subscript =
697 AST.DeclarationNames.getCXXOperatorName(Op: OO_Subscript);
698
699 addHandleAccessFunction(Name&: Subscript, /*IsConst=*/true, /*IsRef=*/true);
700 addHandleAccessFunction(Name&: Subscript, /*IsConst=*/false, /*IsRef=*/true);
701 return *this;
702}
703
704BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addLoadMethods() {
705 if (Record->isCompleteDefinition())
706 return *this;
707
708 ASTContext &AST = Record->getASTContext();
709 IdentifierInfo &II = AST.Idents.get(Name: "Load", TokenCode: tok::TokenKind::identifier);
710 DeclarationName Load(&II);
711 // TODO: We also need versions with status for CheckAccessFullyMapped.
712 addHandleAccessFunction(Name&: Load, /*IsConst=*/false, /*IsRef=*/false);
713
714 return *this;
715}
716
717FieldDecl *BuiltinTypeDeclBuilder::getResourceHandleField() {
718 auto I = Fields.find(Key: "__handle");
719 assert(I != Fields.end() &&
720 I->second->getType()->isHLSLAttributedResourceType() &&
721 "record does not have resource handle field");
722 return I->second;
723}
724
725QualType BuiltinTypeDeclBuilder::getFirstTemplateTypeParam() {
726 assert(Template && "record it not a template");
727 if (const auto *TTD = dyn_cast<TemplateTypeParmDecl>(
728 Template->getTemplateParameters()->getParam(0))) {
729 return QualType(TTD->getTypeForDecl(), 0);
730 }
731 return QualType();
732}
733
734QualType BuiltinTypeDeclBuilder::getHandleElementType() {
735 if (Template)
736 return getFirstTemplateTypeParam();
737 // TODO: Should we default to VoidTy? Using `i8` is arguably ambiguous.
738 return SemaRef.getASTContext().Char8Ty;
739}
740
741// BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::startDefinition() {
742// assert(!Record->isCompleteDefinition() && "record is already complete");
743// Record->startDefinition();
744// return *this;
745// }
746
747BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::completeDefinition() {
748 assert(!Record->isCompleteDefinition() && "record is already complete");
749 assert(Record->isBeingDefined() &&
750 "Definition must be started before completing it.");
751
752 Record->completeDefinition();
753 return *this;
754}
755
756Expr *BuiltinTypeDeclBuilder::getConstantIntExpr(int value) {
757 ASTContext &AST = SemaRef.getASTContext();
758 return IntegerLiteral::Create(
759 AST, llvm::APInt(AST.getTypeSize(AST.IntTy), value, true), AST.IntTy,
760 SourceLocation());
761}
762
763BuiltinTypeDeclBuilder &
764BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
765 ConceptDecl *CD = nullptr) {
766 if (Record->isCompleteDefinition()) {
767 assert(Template && "existing record it not a template");
768 assert(Template->getTemplateParameters()->size() == Names.size() &&
769 "template param count mismatch");
770 return *this;
771 }
772
773 TemplateParameterListBuilder Builder = TemplateParameterListBuilder(*this);
774 for (StringRef Name : Names)
775 Builder.addTypeParameter(Name);
776 return Builder.finalizeTemplateArgs(CD);
777}
778
779BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() {
780 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
781 return BuiltinTypeMethodBuilder(*this, "IncrementCounter",
782 SemaRef.getASTContext().UnsignedIntTy)
783 .callBuiltin(BuiltinName: "__builtin_hlsl_buffer_update_counter", ReturnType: QualType(),
784 ArgSpecs: PH::Handle, ArgSpecs: getConstantIntExpr(value: 1))
785 .finalize();
786}
787
788BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
789 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
790 return BuiltinTypeMethodBuilder(*this, "DecrementCounter",
791 SemaRef.getASTContext().UnsignedIntTy)
792 .callBuiltin(BuiltinName: "__builtin_hlsl_buffer_update_counter", ReturnType: QualType(),
793 ArgSpecs: PH::Handle, ArgSpecs: getConstantIntExpr(value: -1))
794 .finalize();
795}
796
797BuiltinTypeDeclBuilder &
798BuiltinTypeDeclBuilder::addHandleAccessFunction(DeclarationName &Name,
799 bool IsConst, bool IsRef) {
800 assert(!Record->isCompleteDefinition() && "record is already complete");
801 ASTContext &AST = SemaRef.getASTContext();
802 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
803
804 QualType ElemTy = getHandleElementType();
805 QualType AddrSpaceElemTy =
806 AST.getAddrSpaceQualType(T: ElemTy, AddressSpace: LangAS::hlsl_device);
807 QualType ElemPtrTy = AST.getPointerType(T: AddrSpaceElemTy);
808 QualType ReturnTy;
809
810 if (IsRef) {
811 ReturnTy = AddrSpaceElemTy;
812 if (IsConst)
813 ReturnTy.addConst();
814 ReturnTy = AST.getLValueReferenceType(T: ReturnTy);
815 } else {
816 ReturnTy = ElemTy;
817 if (IsConst)
818 ReturnTy.addConst();
819 }
820
821 return BuiltinTypeMethodBuilder(*this, Name, ReturnTy, IsConst)
822 .addParam("Index", AST.UnsignedIntTy)
823 .callBuiltin("__builtin_hlsl_resource_getpointer", ElemPtrTy, PH::Handle,
824 PH::_0)
825 .dereference(PH::LastStmt)
826 .finalize();
827}
828
829BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() {
830 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
831 ASTContext &AST = SemaRef.getASTContext();
832 QualType ElemTy = getHandleElementType();
833 QualType AddrSpaceElemTy =
834 AST.getAddrSpaceQualType(T: ElemTy, AddressSpace: LangAS::hlsl_device);
835 return BuiltinTypeMethodBuilder(*this, "Append", AST.VoidTy)
836 .addParam("value", ElemTy)
837 .callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy,
838 PH::Handle, getConstantIntExpr(value: 1))
839 .callBuiltin("__builtin_hlsl_resource_getpointer",
840 AST.getPointerType(T: AddrSpaceElemTy), PH::Handle,
841 PH::LastStmt)
842 .dereference(PH::LastStmt)
843 .assign(PH::LastStmt, PH::_0)
844 .finalize();
845}
846
847BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() {
848 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
849 ASTContext &AST = SemaRef.getASTContext();
850 QualType ElemTy = getHandleElementType();
851 QualType AddrSpaceElemTy =
852 AST.getAddrSpaceQualType(T: ElemTy, AddressSpace: LangAS::hlsl_device);
853 return BuiltinTypeMethodBuilder(*this, "Consume", ElemTy)
854 .callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy,
855 PH::Handle, getConstantIntExpr(value: -1))
856 .callBuiltin("__builtin_hlsl_resource_getpointer",
857 AST.getPointerType(T: AddrSpaceElemTy), PH::Handle,
858 PH::LastStmt)
859 .dereference(PH::LastStmt)
860 .finalize();
861}
862
863} // namespace hlsl
864} // namespace clang
865

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp