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
27 using namespace llvm::hlsl;
28
29 namespace clang {
30
31 namespace hlsl {
32
33 namespace {
34
lookupBuiltinFunction(Sema & S,StringRef Name)35 static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name) {
36 IdentifierInfo &II =
37 S.getASTContext().Idents.get(Name, 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.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>(R.getFoundDecl());
49 }
50 } // namespace
51
52 // Builder for template arguments of builtin types. Used internally
53 // by BuiltinTypeDeclBuilder.
54 struct TemplateParameterListBuilder {
55 BuiltinTypeDeclBuilder &Builder;
56 llvm::SmallVector<NamedDecl *> Params;
57
TemplateParameterListBuilderclang::hlsl::TemplateParameterListBuilder58 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).
94 struct BuiltinTypeMethodBuilder {
95 private:
96 struct Param {
97 const IdentifierInfo &NameII;
98 QualType Ty;
99 HLSLParamModifierAttr::Spelling Modifier;
Paramclang::hlsl::BuiltinTypeMethodBuilder::Param100 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);
convertPlaceholderclang::hlsl::BuiltinTypeMethodBuilder126 Expr *convertPlaceholder(Expr *E) { return E; }
127
128 public:
129 friend BuiltinTypeDeclBuilder;
130
BuiltinTypeMethodBuilderclang::hlsl::BuiltinTypeMethodBuilder131 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
~BuiltinTypeMethodBuilderclang::hlsl::BuiltinTypeMethodBuilder142 ~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
159 private:
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.
ensureCompleteDeclclang::hlsl::BuiltinTypeMethodBuilder164 void ensureCompleteDecl() {
165 if (!Method)
166 createDecl();
167 }
168 };
169
~TemplateParameterListBuilder()170 TemplateParameterListBuilder::~TemplateParameterListBuilder() {
171 finalizeTemplateArgs();
172 }
173
174 TemplateParameterListBuilder &
addTypeParameter(StringRef Name,QualType DefaultValue)175 TemplateParameterListBuilder::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 AST, Builder.Record->getDeclContext(), SourceLocation(), SourceLocation(),
183 /* TemplateDepth */ 0, Position,
184 &AST.Idents.get(Name, 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.
218 ConceptSpecializationExpr *
constructConceptSpecializationExpr(Sema & S,ConceptDecl * CD)219 TemplateParameterListBuilder::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 Context, // AST context
241 Builder.Record->getDeclContext(), // DeclContext
242 SourceLocation(), 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 Context, Builder.Record->getDeclContext(), Loc, {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(ConceptTA, QualType(), SourceLocation());
275
276 TALI.addArgument(TAL);
277 const ASTTemplateArgumentListInfo *ATALI =
278 ASTTemplateArgumentListInfo::Create(Context, 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(Context, CR, ImplicitCSEDecl, &CS);
287
288 return CSE;
289 }
290
291 BuiltinTypeDeclBuilder &
finalizeTemplateArgs(ConceptDecl * CD)292 TemplateParameterListBuilder::finalizeTemplateArgs(ConceptDecl *CD) {
293 if (Params.empty())
294 return Builder;
295
296 ASTContext &AST = Builder.SemaRef.Context;
297 ConceptSpecializationExpr *CSE =
298 CD ? constructConceptSpecializationExpr(Builder.SemaRef, CD) : nullptr;
299 auto *ParamList = TemplateParameterList::Create(
300 AST, SourceLocation(), SourceLocation(), Params, SourceLocation(), CSE);
301 Builder.Template = ClassTemplateDecl::Create(
302 AST, Builder.Record->getDeclContext(), SourceLocation(),
303 DeclarationName(Builder.Record->getIdentifier()), ParamList,
304 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(Builder.Record, T);
318
319 return Builder;
320 }
321
convertPlaceholder(PlaceHolder PH)322 Expr *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>(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
BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder & DB,StringRef NameStr,QualType ReturnTy,bool IsConst,bool IsCtor)341 BuiltinTypeMethodBuilder::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 DB.Record->getTypeForDecl()->getCanonicalTypeUnqualified());
355 } else {
356 const IdentifierInfo &II =
357 AST.Idents.get(NameStr, tok::TokenKind::identifier);
358 Name = DeclarationName(&II);
359 }
360 }
361
362 BuiltinTypeMethodBuilder &
addParam(StringRef Name,QualType Ty,HLSLParamModifierAttr::Spelling Modifier)363 BuiltinTypeMethodBuilder::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, tok::TokenKind::identifier);
368 Params.emplace_back(II, Ty, Modifier);
369 return *this;
370 }
371
createDecl()372 void 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(FuncTy, SourceLocation());
389 DeclarationNameInfo NameInfo = DeclarationNameInfo(Name, SourceLocation());
390 if (IsCtor)
391 Method = CXXConstructorDecl::Create(
392 AST, DeclBuilder.Record, SourceLocation(), NameInfo, FuncTy, TSInfo,
393 ExplicitSpecifier(), false, true, false,
394 ConstexprSpecKind::Unspecified);
395 else
396 Method =
397 CXXMethodDecl::Create(AST, DeclBuilder.Record, SourceLocation(),
398 NameInfo, FuncTy, TSInfo, SC_None, false, false,
399 ConstexprSpecKind::Unspecified, 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 AST, Method->getDeclContext(), SourceLocation(), SourceLocation(),
410 &MP.NameII, MP.Ty,
411 AST.getTrivialTypeSourceInfo(MP.Ty, SourceLocation()), SC_None,
412 nullptr);
413 if (MP.Modifier != HLSLParamModifierAttr::Keyword_in) {
414 auto *Mod =
415 HLSLParamModifierAttr::Create(AST, SourceRange(), MP.Modifier);
416 Parm->addAttr(Mod);
417 }
418 Parm->setScopeInfo(CurScopeDepth, I);
419 ParmDecls.push_back(Parm);
420 FnProtoLoc.setParam(I, Parm);
421 }
422 Method->setParams({ParmDecls});
423 }
424
getResourceHandleExpr()425 Expr *BuiltinTypeMethodBuilder::getResourceHandleExpr() {
426 ensureCompleteDecl();
427
428 ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
429 CXXThisExpr *This = CXXThisExpr::Create(
430 AST, SourceLocation(), Method->getFunctionObjectParameterType(), true);
431 FieldDecl *HandleField = DeclBuilder.getResourceHandleField();
432 return MemberExpr::CreateImplicit(AST, This, false, HandleField,
433 HandleField->getType(), VK_LValue,
434 OK_Ordinary);
435 }
436
437 template <typename... Ts>
438 BuiltinTypeMethodBuilder &
callBuiltin(StringRef BuiltinName,QualType ReturnType,Ts...ArgSpecs)439 BuiltinTypeMethodBuilder::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(DeclBuilder.SemaRef, 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 AST, AST.getPointerType(FD->getType()), CK_BuiltinFnToFnPtr, DRE, nullptr,
454 VK_PRValue, FPOptionsOverride());
455
456 if (ReturnType.isNull())
457 ReturnType = FD->getReturnType();
458
459 Expr *Call = CallExpr::Create(AST, ImpCast, Args, ReturnType, VK_PRValue,
460 SourceLocation(), FPOptionsOverride());
461 StmtsList.push_back(Call);
462 return *this;
463 }
464
465 template <typename TLHS, typename TRHS>
assign(TLHS LHS,TRHS RHS)466 BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::assign(TLHS LHS, TRHS RHS) {
467 Expr *LHSExpr = convertPlaceholder(LHS);
468 Expr *RHSExpr = convertPlaceholder(RHS);
469 Stmt *AssignStmt = BinaryOperator::Create(
470 DeclBuilder.SemaRef.getASTContext(), LHSExpr, RHSExpr, BO_Assign,
471 LHSExpr->getType(), ExprValueKind::VK_PRValue,
472 ExprObjectKind::OK_Ordinary, SourceLocation(), FPOptionsOverride());
473 StmtsList.push_back(AssignStmt);
474 return *this;
475 }
476
477 template <typename T>
dereference(T Ptr)478 BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::dereference(T Ptr) {
479 Expr *PtrExpr = convertPlaceholder(Ptr);
480 Expr *Deref =
481 UnaryOperator::Create(DeclBuilder.SemaRef.getASTContext(), PtrExpr,
482 UO_Deref, PtrExpr->getType()->getPointeeType(),
483 VK_PRValue, OK_Ordinary, SourceLocation(),
484 /*CanOverflow=*/false, FPOptionsOverride());
485 StmtsList.push_back(Deref);
486 return *this;
487 }
488
finalize()489 BuiltinTypeDeclBuilder &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>(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>(LastExpr)) {
506 StmtsList.pop_back();
507 StmtsList.push_back(
508 ReturnStmt::Create(AST, SourceLocation(), LastExpr, nullptr));
509 }
510 }
511 }
512
513 Method->setBody(CompoundStmt::Create(AST, StmtsList, FPOptionsOverride(),
514 SourceLocation(), 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
BuiltinTypeDeclBuilder(Sema & SemaRef,CXXRecordDecl * R)524 BuiltinTypeDeclBuilder::BuiltinTypeDeclBuilder(Sema &SemaRef, CXXRecordDecl *R)
525 : SemaRef(SemaRef), Record(R) {
526 Record->startDefinition();
527 Template = Record->getDescribedClassTemplate();
528 }
529
BuiltinTypeDeclBuilder(Sema & SemaRef,NamespaceDecl * Namespace,StringRef Name)530 BuiltinTypeDeclBuilder::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, 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>(Found)) {
543 PrevDecl = TD->getTemplatedDecl();
544 PrevTemplate = TD;
545 } else
546 PrevDecl = dyn_cast<CXXRecordDecl>(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
~BuiltinTypeDeclBuilder()568 BuiltinTypeDeclBuilder::~BuiltinTypeDeclBuilder() {
569 if (HLSLNamespace && !Template && Record->getDeclContext() == HLSLNamespace)
570 HLSLNamespace->addDecl(Record);
571 }
572
finalizeForwardDeclaration()573 CXXRecordDecl *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
585 BuiltinTypeDeclBuilder &
addMemberVariable(StringRef Name,QualType Type,llvm::ArrayRef<Attr * > Attrs,AccessSpecifier Access)586 BuiltinTypeDeclBuilder::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, tok::TokenKind::identifier);
595 TypeSourceInfo *MemTySource =
596 AST.getTrivialTypeSourceInfo(Type, 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(Field);
608 Fields[Name] = Field;
609 return *this;
610 }
611
addHandleMember(ResourceClass RC,bool IsROV,bool RawBuffer,AccessSpecifier Access)612 BuiltinTypeDeclBuilder &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(getHandleElementType(), 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("__handle", AttributedResTy, {}, Access);
632 return *this;
633 }
634
635 // Adds default constructor to the resource class:
636 // Resource::Resource()
addDefaultHandleConstructor()637 BuiltinTypeDeclBuilder &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("__builtin_hlsl_resource_uninitializedhandle", HandleType,
646 PH::Handle)
647 .assign(PH::Handle, PH::LastStmt)
648 .finalize();
649 }
650
651 BuiltinTypeDeclBuilder &
addHandleConstructorFromBinding()652 BuiltinTypeDeclBuilder::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
672 BuiltinTypeDeclBuilder &
addHandleConstructorFromImplicitBinding()673 BuiltinTypeDeclBuilder::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
addArraySubscriptOperators()694 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() {
695 ASTContext &AST = Record->getASTContext();
696 DeclarationName Subscript =
697 AST.DeclarationNames.getCXXOperatorName(OO_Subscript);
698
699 addHandleAccessFunction(Subscript, /*IsConst=*/true, /*IsRef=*/true);
700 if (getResourceAttrs().ResourceClass == llvm::dxil::ResourceClass::UAV)
701 addHandleAccessFunction(Subscript, /*IsConst=*/false, /*IsRef=*/true);
702
703 return *this;
704 }
705
addLoadMethods()706 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addLoadMethods() {
707 if (Record->isCompleteDefinition())
708 return *this;
709
710 ASTContext &AST = Record->getASTContext();
711 IdentifierInfo &II = AST.Idents.get("Load", tok::TokenKind::identifier);
712 DeclarationName Load(&II);
713 // TODO: We also need versions with status for CheckAccessFullyMapped.
714 addHandleAccessFunction(Load, /*IsConst=*/false, /*IsRef=*/false);
715
716 return *this;
717 }
718
getResourceHandleField() const719 FieldDecl *BuiltinTypeDeclBuilder::getResourceHandleField() const {
720 auto I = Fields.find("__handle");
721 assert(I != Fields.end() &&
722 I->second->getType()->isHLSLAttributedResourceType() &&
723 "record does not have resource handle field");
724 return I->second;
725 }
726
getFirstTemplateTypeParam()727 QualType BuiltinTypeDeclBuilder::getFirstTemplateTypeParam() {
728 assert(Template && "record it not a template");
729 if (const auto *TTD = dyn_cast<TemplateTypeParmDecl>(
730 Template->getTemplateParameters()->getParam(0))) {
731 return QualType(TTD->getTypeForDecl(), 0);
732 }
733 return QualType();
734 }
735
getHandleElementType()736 QualType BuiltinTypeDeclBuilder::getHandleElementType() {
737 if (Template)
738 return getFirstTemplateTypeParam();
739 // TODO: Should we default to VoidTy? Using `i8` is arguably ambiguous.
740 return SemaRef.getASTContext().Char8Ty;
741 }
742
743 HLSLAttributedResourceType::Attributes
getResourceAttrs() const744 BuiltinTypeDeclBuilder::getResourceAttrs() const {
745 QualType HandleType = getResourceHandleField()->getType();
746 return cast<HLSLAttributedResourceType>(HandleType)->getAttrs();
747 }
748
749 // BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::startDefinition() {
750 // assert(!Record->isCompleteDefinition() && "record is already complete");
751 // Record->startDefinition();
752 // return *this;
753 // }
754
completeDefinition()755 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::completeDefinition() {
756 assert(!Record->isCompleteDefinition() && "record is already complete");
757 assert(Record->isBeingDefined() &&
758 "Definition must be started before completing it.");
759
760 Record->completeDefinition();
761 return *this;
762 }
763
getConstantIntExpr(int value)764 Expr *BuiltinTypeDeclBuilder::getConstantIntExpr(int value) {
765 ASTContext &AST = SemaRef.getASTContext();
766 return IntegerLiteral::Create(
767 AST, llvm::APInt(AST.getTypeSize(AST.IntTy), value, true), AST.IntTy,
768 SourceLocation());
769 }
770
771 BuiltinTypeDeclBuilder &
addSimpleTemplateParams(ArrayRef<StringRef> Names,ConceptDecl * CD=nullptr)772 BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
773 ConceptDecl *CD = nullptr) {
774 if (Record->isCompleteDefinition()) {
775 assert(Template && "existing record it not a template");
776 assert(Template->getTemplateParameters()->size() == Names.size() &&
777 "template param count mismatch");
778 return *this;
779 }
780
781 TemplateParameterListBuilder Builder = TemplateParameterListBuilder(*this);
782 for (StringRef Name : Names)
783 Builder.addTypeParameter(Name);
784 return Builder.finalizeTemplateArgs(CD);
785 }
786
addIncrementCounterMethod()787 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() {
788 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
789 return BuiltinTypeMethodBuilder(*this, "IncrementCounter",
790 SemaRef.getASTContext().UnsignedIntTy)
791 .callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
792 PH::Handle, getConstantIntExpr(1))
793 .finalize();
794 }
795
addDecrementCounterMethod()796 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
797 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
798 return BuiltinTypeMethodBuilder(*this, "DecrementCounter",
799 SemaRef.getASTContext().UnsignedIntTy)
800 .callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
801 PH::Handle, getConstantIntExpr(-1))
802 .finalize();
803 }
804
805 BuiltinTypeDeclBuilder &
addHandleAccessFunction(DeclarationName & Name,bool IsConst,bool IsRef)806 BuiltinTypeDeclBuilder::addHandleAccessFunction(DeclarationName &Name,
807 bool IsConst, bool IsRef) {
808 assert(!Record->isCompleteDefinition() && "record is already complete");
809 ASTContext &AST = SemaRef.getASTContext();
810 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
811
812 QualType ElemTy = getHandleElementType();
813 QualType AddrSpaceElemTy =
814 AST.getAddrSpaceQualType(ElemTy, LangAS::hlsl_device);
815 QualType ElemPtrTy = AST.getPointerType(AddrSpaceElemTy);
816 QualType ReturnTy;
817
818 if (IsRef) {
819 ReturnTy = AddrSpaceElemTy;
820 if (IsConst)
821 ReturnTy.addConst();
822 ReturnTy = AST.getLValueReferenceType(ReturnTy);
823 } else {
824 ReturnTy = ElemTy;
825 if (IsConst)
826 ReturnTy.addConst();
827 }
828
829 return BuiltinTypeMethodBuilder(*this, Name, ReturnTy, IsConst)
830 .addParam("Index", AST.UnsignedIntTy)
831 .callBuiltin("__builtin_hlsl_resource_getpointer", ElemPtrTy, PH::Handle,
832 PH::_0)
833 .dereference(PH::LastStmt)
834 .finalize();
835 }
836
addAppendMethod()837 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() {
838 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
839 ASTContext &AST = SemaRef.getASTContext();
840 QualType ElemTy = getHandleElementType();
841 QualType AddrSpaceElemTy =
842 AST.getAddrSpaceQualType(ElemTy, LangAS::hlsl_device);
843 return BuiltinTypeMethodBuilder(*this, "Append", AST.VoidTy)
844 .addParam("value", ElemTy)
845 .callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy,
846 PH::Handle, getConstantIntExpr(1))
847 .callBuiltin("__builtin_hlsl_resource_getpointer",
848 AST.getPointerType(AddrSpaceElemTy), PH::Handle,
849 PH::LastStmt)
850 .dereference(PH::LastStmt)
851 .assign(PH::LastStmt, PH::_0)
852 .finalize();
853 }
854
addConsumeMethod()855 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() {
856 using PH = BuiltinTypeMethodBuilder::PlaceHolder;
857 ASTContext &AST = SemaRef.getASTContext();
858 QualType ElemTy = getHandleElementType();
859 QualType AddrSpaceElemTy =
860 AST.getAddrSpaceQualType(ElemTy, LangAS::hlsl_device);
861 return BuiltinTypeMethodBuilder(*this, "Consume", ElemTy)
862 .callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy,
863 PH::Handle, getConstantIntExpr(-1))
864 .callBuiltin("__builtin_hlsl_resource_getpointer",
865 AST.getPointerType(AddrSpaceElemTy), PH::Handle,
866 PH::LastStmt)
867 .dereference(PH::LastStmt)
868 .finalize();
869 }
870
871 } // namespace hlsl
872 } // namespace clang
873