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 | |
35 | static 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. |
54 | struct 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). |
94 | struct BuiltinTypeMethodBuilder { |
95 | private: |
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 | |
128 | public: |
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 | |
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. |
164 | void ensureCompleteDecl() { |
165 | if (!Method) |
166 | createDecl(); |
167 | } |
168 | }; |
169 | |
170 | TemplateParameterListBuilder::~TemplateParameterListBuilder() { |
171 | finalizeTemplateArgs(); |
172 | } |
173 | |
174 | TemplateParameterListBuilder & |
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 | 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. |
218 | ConceptSpecializationExpr * |
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 | 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 | |
291 | BuiltinTypeDeclBuilder & |
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(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 | |
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>(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 | |
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 | 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 | |
362 | BuiltinTypeMethodBuilder & |
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, TokenCode: tok::TokenKind::identifier); |
368 | Params.emplace_back(II, Ty, Modifier); |
369 | return *this; |
370 | } |
371 | |
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(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 | |
425 | Expr *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 | |
437 | template <typename... Ts> |
438 | BuiltinTypeMethodBuilder & |
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(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 | |
465 | template <typename TLHS, typename TRHS> |
466 | BuiltinTypeMethodBuilder &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 | |
477 | template <typename T> |
478 | BuiltinTypeMethodBuilder &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 | |
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>(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 | |
524 | BuiltinTypeDeclBuilder::BuiltinTypeDeclBuilder(Sema &SemaRef, CXXRecordDecl *R) |
525 | : SemaRef(SemaRef), Record(R) { |
526 | Record->startDefinition(); |
527 | Template = Record->getDescribedClassTemplate(); |
528 | } |
529 | |
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, 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 | |
568 | BuiltinTypeDeclBuilder::~BuiltinTypeDeclBuilder() { |
569 | if (HLSLNamespace && !Template && Record->getDeclContext() == HLSLNamespace) |
570 | HLSLNamespace->addDecl(Record); |
571 | } |
572 | |
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 & |
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, 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 | |
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(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() |
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(BuiltinName: "__builtin_hlsl_resource_uninitializedhandle", ReturnType: HandleType, |
646 | ArgSpecs: PH::Handle) |
647 | .assign(LHS: PH::Handle, RHS: PH::LastStmt) |
648 | .finalize(); |
649 | } |
650 | |
651 | BuiltinTypeDeclBuilder & |
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 & |
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 | |
694 | BuiltinTypeDeclBuilder &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 | |
704 | BuiltinTypeDeclBuilder &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 | |
717 | FieldDecl *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 | |
725 | QualType 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 | |
734 | QualType 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 | |
747 | BuiltinTypeDeclBuilder &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 | |
756 | Expr *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 | |
763 | BuiltinTypeDeclBuilder & |
764 | BuiltinTypeDeclBuilder::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 | |
779 | BuiltinTypeDeclBuilder &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 | |
788 | BuiltinTypeDeclBuilder &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 | |
797 | BuiltinTypeDeclBuilder & |
798 | BuiltinTypeDeclBuilder::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 | |
829 | BuiltinTypeDeclBuilder &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 | |
847 | BuiltinTypeDeclBuilder &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 |
Definitions
- lookupBuiltinFunction
- TemplateParameterListBuilder
- TemplateParameterListBuilder
- BuiltinTypeMethodBuilder
- Param
- Param
- PlaceHolder
- convertPlaceholder
- BuiltinTypeMethodBuilder
- BuiltinTypeMethodBuilder
- ~BuiltinTypeMethodBuilder
- operator=
- ensureCompleteDecl
- ~TemplateParameterListBuilder
- addTypeParameter
- constructConceptSpecializationExpr
- finalizeTemplateArgs
- convertPlaceholder
- BuiltinTypeMethodBuilder
- addParam
- createDecl
- getResourceHandleExpr
- callBuiltin
- assign
- dereference
- finalize
- BuiltinTypeDeclBuilder
- BuiltinTypeDeclBuilder
- ~BuiltinTypeDeclBuilder
- finalizeForwardDeclaration
- addMemberVariable
- addHandleMember
- addDefaultHandleConstructor
- addHandleConstructorFromBinding
- addHandleConstructorFromImplicitBinding
- addArraySubscriptOperators
- addLoadMethods
- getResourceHandleField
- getFirstTemplateTypeParam
- getHandleElementType
- completeDefinition
- getConstantIntExpr
- addSimpleTemplateParams
- addIncrementCounterMethod
- addDecrementCounterMethod
- addHandleAccessFunction
- addAppendMethod
Learn to use CMake with our Intro Training
Find out more