1//===--- HLSLExternalSemaSource.cpp - HLSL Sema Source --------------------===//
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//
10//===----------------------------------------------------------------------===//
11
12#include "clang/Sema/HLSLExternalSemaSource.h"
13#include "HLSLBuiltinTypeDeclBuilder.h"
14#include "clang/AST/ASTContext.h"
15#include "clang/AST/Attr.h"
16#include "clang/AST/Decl.h"
17#include "clang/AST/DeclCXX.h"
18#include "clang/AST/Expr.h"
19#include "clang/AST/Type.h"
20#include "clang/Basic/SourceLocation.h"
21#include "clang/Sema/Lookup.h"
22#include "clang/Sema/Sema.h"
23#include "clang/Sema/SemaHLSL.h"
24#include "llvm/ADT/SmallVector.h"
25
26using namespace clang;
27using namespace llvm::hlsl;
28
29using clang::hlsl::BuiltinTypeDeclBuilder;
30
31void HLSLExternalSemaSource::InitializeSema(Sema &S) {
32 SemaPtr = &S;
33 ASTContext &AST = SemaPtr->getASTContext();
34 // If the translation unit has external storage force external decls to load.
35 if (AST.getTranslationUnitDecl()->hasExternalLexicalStorage())
36 (void)AST.getTranslationUnitDecl()->decls_begin();
37
38 IdentifierInfo &HLSL = AST.Idents.get(Name: "hlsl", TokenCode: tok::TokenKind::identifier);
39 LookupResult Result(S, &HLSL, SourceLocation(), Sema::LookupNamespaceName);
40 NamespaceDecl *PrevDecl = nullptr;
41 if (S.LookupQualifiedName(Result, AST.getTranslationUnitDecl()))
42 PrevDecl = Result.getAsSingle<NamespaceDecl>();
43 HLSLNamespace = NamespaceDecl::Create(
44 AST, AST.getTranslationUnitDecl(), /*Inline=*/false, SourceLocation(),
45 SourceLocation(), &HLSL, PrevDecl, /*Nested=*/false);
46 HLSLNamespace->setImplicit(true);
47 HLSLNamespace->setHasExternalLexicalStorage();
48 AST.getTranslationUnitDecl()->addDecl(HLSLNamespace);
49
50 // Force external decls in the HLSL namespace to load from the PCH.
51 (void)HLSLNamespace->getCanonicalDecl()->decls_begin();
52 defineTrivialHLSLTypes();
53 defineHLSLTypesWithForwardDeclarations();
54
55 // This adds a `using namespace hlsl` directive. In DXC, we don't put HLSL's
56 // built in types inside a namespace, but we are planning to change that in
57 // the near future. In order to be source compatible older versions of HLSL
58 // will need to implicitly use the hlsl namespace. For now in clang everything
59 // will get added to the namespace, and we can remove the using directive for
60 // future language versions to match HLSL's evolution.
61 auto *UsingDecl = UsingDirectiveDecl::Create(
62 AST, AST.getTranslationUnitDecl(), SourceLocation(), SourceLocation(),
63 NestedNameSpecifierLoc(), SourceLocation(), HLSLNamespace,
64 AST.getTranslationUnitDecl());
65
66 AST.getTranslationUnitDecl()->addDecl(D: UsingDecl);
67}
68
69void HLSLExternalSemaSource::defineHLSLVectorAlias() {
70 ASTContext &AST = SemaPtr->getASTContext();
71
72 llvm::SmallVector<NamedDecl *> TemplateParams;
73
74 auto *TypeParam = TemplateTypeParmDecl::Create(
75 AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 0,
76 &AST.Idents.get(Name: "element", TokenCode: tok::TokenKind::identifier), false, false);
77 TypeParam->setDefaultArgument(
78 AST, SemaPtr->getTrivialTemplateArgumentLoc(
79 Arg: TemplateArgument(AST.FloatTy), NTTPType: QualType(), Loc: SourceLocation()));
80
81 TemplateParams.emplace_back(TypeParam);
82
83 auto *SizeParam = NonTypeTemplateParmDecl::Create(
84 AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 1,
85 &AST.Idents.get(Name: "element_count", TokenCode: tok::TokenKind::identifier), AST.IntTy,
86 false, AST.getTrivialTypeSourceInfo(T: AST.IntTy));
87 llvm::APInt Val(AST.getIntWidth(T: AST.IntTy), 4);
88 TemplateArgument Default(AST, llvm::APSInt(std::move(Val)), AST.IntTy,
89 /*IsDefaulted=*/true);
90 SizeParam->setDefaultArgument(
91 AST, SemaPtr->getTrivialTemplateArgumentLoc(Arg: Default, NTTPType: AST.IntTy,
92 Loc: SourceLocation(), TemplateParam: SizeParam));
93 TemplateParams.emplace_back(SizeParam);
94
95 auto *ParamList =
96 TemplateParameterList::Create(C: AST, TemplateLoc: SourceLocation(), LAngleLoc: SourceLocation(),
97 Params: TemplateParams, RAngleLoc: SourceLocation(), RequiresClause: nullptr);
98
99 IdentifierInfo &II = AST.Idents.get(Name: "vector", TokenCode: tok::TokenKind::identifier);
100
101 QualType AliasType = AST.getDependentSizedExtVectorType(
102 VectorType: AST.getTemplateTypeParmType(Depth: 0, Index: 0, ParameterPack: false, ParmDecl: TypeParam),
103 SizeExpr: DeclRefExpr::Create(
104 AST, NestedNameSpecifierLoc(), SourceLocation(), SizeParam, false,
105 DeclarationNameInfo(SizeParam->getDeclName(), SourceLocation()),
106 AST.IntTy, VK_LValue),
107 AttrLoc: SourceLocation());
108
109 auto *Record = TypeAliasDecl::Create(AST, HLSLNamespace, SourceLocation(),
110 SourceLocation(), &II,
111 AST.getTrivialTypeSourceInfo(T: AliasType));
112 Record->setImplicit(true);
113
114 auto *Template =
115 TypeAliasTemplateDecl::Create(C&: AST, DC: HLSLNamespace, L: SourceLocation(),
116 Name: Record->getIdentifier(), Params: ParamList, Decl: Record);
117
118 Record->setDescribedAliasTemplate(Template);
119 Template->setImplicit(true);
120 Template->setLexicalDeclContext(Record->getDeclContext());
121 HLSLNamespace->addDecl(D: Template);
122}
123
124void HLSLExternalSemaSource::defineTrivialHLSLTypes() {
125 defineHLSLVectorAlias();
126}
127
128/// Set up common members and attributes for buffer types
129static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
130 ResourceClass RC, bool IsROV,
131 bool RawBuffer) {
132 return BuiltinTypeDeclBuilder(S, Decl)
133 .addHandleMember(RC, IsROV, RawBuffer)
134 .addDefaultHandleConstructor()
135 .addHandleConstructorFromBinding()
136 .addHandleConstructorFromImplicitBinding();
137}
138
139// This function is responsible for constructing the constraint expression for
140// this concept:
141// template<typename T> concept is_typed_resource_element_compatible =
142// __is_typed_resource_element_compatible<T>;
143static Expr *constructTypedBufferConstraintExpr(Sema &S, SourceLocation NameLoc,
144 TemplateTypeParmDecl *T) {
145 ASTContext &Context = S.getASTContext();
146
147 // Obtain the QualType for 'bool'
148 QualType BoolTy = Context.BoolTy;
149
150 // Create a QualType that points to this TemplateTypeParmDecl
151 QualType TType = Context.getTypeDeclType(T);
152
153 // Create a TypeSourceInfo for the template type parameter 'T'
154 TypeSourceInfo *TTypeSourceInfo =
155 Context.getTrivialTypeSourceInfo(T: TType, Loc: NameLoc);
156
157 TypeTraitExpr *TypedResExpr = TypeTraitExpr::Create(
158 C: Context, T: BoolTy, Loc: NameLoc, Kind: UTT_IsTypedResourceElementCompatible,
159 Args: {TTypeSourceInfo}, RParenLoc: NameLoc, Value: true);
160
161 return TypedResExpr;
162}
163
164// This function is responsible for constructing the constraint expression for
165// this concept:
166// template<typename T> concept is_structured_resource_element_compatible =
167// !__is_intangible<T> && sizeof(T) >= 1;
168static Expr *constructStructuredBufferConstraintExpr(Sema &S,
169 SourceLocation NameLoc,
170 TemplateTypeParmDecl *T) {
171 ASTContext &Context = S.getASTContext();
172
173 // Obtain the QualType for 'bool'
174 QualType BoolTy = Context.BoolTy;
175
176 // Create a QualType that points to this TemplateTypeParmDecl
177 QualType TType = Context.getTypeDeclType(T);
178
179 // Create a TypeSourceInfo for the template type parameter 'T'
180 TypeSourceInfo *TTypeSourceInfo =
181 Context.getTrivialTypeSourceInfo(T: TType, Loc: NameLoc);
182
183 TypeTraitExpr *IsIntangibleExpr =
184 TypeTraitExpr::Create(C: Context, T: BoolTy, Loc: NameLoc, Kind: UTT_IsIntangibleType,
185 Args: {TTypeSourceInfo}, RParenLoc: NameLoc, Value: true);
186
187 // negate IsIntangibleExpr
188 UnaryOperator *NotIntangibleExpr = UnaryOperator::Create(
189 Context, IsIntangibleExpr, UO_LNot, BoolTy, VK_LValue, OK_Ordinary,
190 NameLoc, false, FPOptionsOverride());
191
192 // element types also may not be of 0 size
193 UnaryExprOrTypeTraitExpr *SizeOfExpr = new (Context) UnaryExprOrTypeTraitExpr(
194 UETT_SizeOf, TTypeSourceInfo, BoolTy, NameLoc, NameLoc);
195
196 // Create a BinaryOperator that checks if the size of the type is not equal to
197 // 1 Empty structs have a size of 1 in HLSL, so we need to check for that
198 IntegerLiteral *rhs = IntegerLiteral::Create(
199 C: Context, V: llvm::APInt(Context.getTypeSize(T: Context.getSizeType()), 1, true),
200 type: Context.getSizeType(), l: NameLoc);
201
202 BinaryOperator *SizeGEQOneExpr =
203 BinaryOperator::Create(Context, SizeOfExpr, rhs, BO_GE, BoolTy, VK_LValue,
204 OK_Ordinary, NameLoc, FPOptionsOverride());
205
206 // Combine the two constraints
207 BinaryOperator *CombinedExpr = BinaryOperator::Create(
208 Context, NotIntangibleExpr, SizeGEQOneExpr, BO_LAnd, BoolTy, VK_LValue,
209 OK_Ordinary, NameLoc, FPOptionsOverride());
210
211 return CombinedExpr;
212}
213
214static ConceptDecl *constructBufferConceptDecl(Sema &S, NamespaceDecl *NSD,
215 bool isTypedBuffer) {
216 ASTContext &Context = S.getASTContext();
217 DeclContext *DC = NSD->getDeclContext();
218 SourceLocation DeclLoc = SourceLocation();
219
220 IdentifierInfo &ElementTypeII = Context.Idents.get(Name: "element_type");
221 TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create(
222 C: Context, DC: NSD->getDeclContext(), KeyLoc: DeclLoc, NameLoc: DeclLoc,
223 /*D=*/0,
224 /*P=*/0,
225 /*Id=*/&ElementTypeII,
226 /*Typename=*/true,
227 /*ParameterPack=*/false);
228
229 T->setDeclContext(DC);
230 T->setReferenced();
231
232 // Create and Attach Template Parameter List to ConceptDecl
233 TemplateParameterList *ConceptParams = TemplateParameterList::Create(
234 Context, DeclLoc, DeclLoc, {T}, DeclLoc, nullptr);
235
236 DeclarationName DeclName;
237 Expr *ConstraintExpr = nullptr;
238
239 if (isTypedBuffer) {
240 DeclName = DeclarationName(
241 &Context.Idents.get(Name: "__is_typed_resource_element_compatible"));
242 ConstraintExpr = constructTypedBufferConstraintExpr(S, NameLoc: DeclLoc, T);
243 } else {
244 DeclName = DeclarationName(
245 &Context.Idents.get(Name: "__is_structured_resource_element_compatible"));
246 ConstraintExpr = constructStructuredBufferConstraintExpr(S, NameLoc: DeclLoc, T);
247 }
248
249 // Create a ConceptDecl
250 ConceptDecl *CD =
251 ConceptDecl::Create(C&: Context, DC: NSD->getDeclContext(), L: DeclLoc, Name: DeclName,
252 Params: ConceptParams, ConstraintExpr);
253
254 // Attach the template parameter list to the ConceptDecl
255 CD->setTemplateParameters(ConceptParams);
256
257 // Add the concept declaration to the Translation Unit Decl
258 NSD->getDeclContext()->addDecl(CD);
259
260 return CD;
261}
262
263void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
264 CXXRecordDecl *Decl;
265 ConceptDecl *TypedBufferConcept = constructBufferConceptDecl(
266 S&: *SemaPtr, NSD: HLSLNamespace, /*isTypedBuffer*/ true);
267 ConceptDecl *StructuredBufferConcept = constructBufferConceptDecl(
268 S&: *SemaPtr, NSD: HLSLNamespace, /*isTypedBuffer*/ false);
269
270 Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "Buffer")
271 .addSimpleTemplateParams(Names: {"element_type"}, CD: TypedBufferConcept)
272 .finalizeForwardDeclaration();
273
274 onCompletion(Record: Decl, Fn: [this](CXXRecordDecl *Decl) {
275 setupBufferType(Decl, S&: *SemaPtr, RC: ResourceClass::SRV, /*IsROV=*/false,
276 /*RawBuffer=*/false)
277 .addArraySubscriptOperators()
278 .addLoadMethods()
279 .completeDefinition();
280 });
281
282 Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
283 .addSimpleTemplateParams(Names: {"element_type"}, CD: TypedBufferConcept)
284 .finalizeForwardDeclaration();
285
286 onCompletion(Record: Decl, Fn: [this](CXXRecordDecl *Decl) {
287 setupBufferType(Decl, S&: *SemaPtr, RC: ResourceClass::UAV, /*IsROV=*/false,
288 /*RawBuffer=*/false)
289 .addArraySubscriptOperators()
290 .addLoadMethods()
291 .completeDefinition();
292 });
293
294 Decl =
295 BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer")
296 .addSimpleTemplateParams(Names: {"element_type"}, CD: StructuredBufferConcept)
297 .finalizeForwardDeclaration();
298 onCompletion(Record: Decl, Fn: [this](CXXRecordDecl *Decl) {
299 setupBufferType(Decl, S&: *SemaPtr, RC: ResourceClass::UAV, /*IsROV=*/true,
300 /*RawBuffer=*/false)
301 .addArraySubscriptOperators()
302 .addLoadMethods()
303 .completeDefinition();
304 });
305
306 Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "StructuredBuffer")
307 .addSimpleTemplateParams(Names: {"element_type"}, CD: StructuredBufferConcept)
308 .finalizeForwardDeclaration();
309 onCompletion(Record: Decl, Fn: [this](CXXRecordDecl *Decl) {
310 setupBufferType(Decl, S&: *SemaPtr, RC: ResourceClass::SRV, /*IsROV=*/false,
311 /*RawBuffer=*/true)
312 .addArraySubscriptOperators()
313 .addLoadMethods()
314 .completeDefinition();
315 });
316
317 Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWStructuredBuffer")
318 .addSimpleTemplateParams(Names: {"element_type"}, CD: StructuredBufferConcept)
319 .finalizeForwardDeclaration();
320 onCompletion(Record: Decl, Fn: [this](CXXRecordDecl *Decl) {
321 setupBufferType(Decl, S&: *SemaPtr, RC: ResourceClass::UAV, /*IsROV=*/false,
322 /*RawBuffer=*/true)
323 .addArraySubscriptOperators()
324 .addLoadMethods()
325 .addIncrementCounterMethod()
326 .addDecrementCounterMethod()
327 .completeDefinition();
328 });
329
330 Decl =
331 BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "AppendStructuredBuffer")
332 .addSimpleTemplateParams(Names: {"element_type"}, CD: StructuredBufferConcept)
333 .finalizeForwardDeclaration();
334 onCompletion(Record: Decl, Fn: [this](CXXRecordDecl *Decl) {
335 setupBufferType(Decl, S&: *SemaPtr, RC: ResourceClass::UAV, /*IsROV=*/false,
336 /*RawBuffer=*/true)
337 .addAppendMethod()
338 .completeDefinition();
339 });
340
341 Decl =
342 BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "ConsumeStructuredBuffer")
343 .addSimpleTemplateParams(Names: {"element_type"}, CD: StructuredBufferConcept)
344 .finalizeForwardDeclaration();
345 onCompletion(Record: Decl, Fn: [this](CXXRecordDecl *Decl) {
346 setupBufferType(Decl, S&: *SemaPtr, RC: ResourceClass::UAV, /*IsROV=*/false,
347 /*RawBuffer=*/true)
348 .addConsumeMethod()
349 .completeDefinition();
350 });
351
352 Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace,
353 "RasterizerOrderedStructuredBuffer")
354 .addSimpleTemplateParams(Names: {"element_type"}, CD: StructuredBufferConcept)
355 .finalizeForwardDeclaration();
356 onCompletion(Record: Decl, Fn: [this](CXXRecordDecl *Decl) {
357 setupBufferType(Decl, S&: *SemaPtr, RC: ResourceClass::UAV, /*IsROV=*/true,
358 /*RawBuffer=*/true)
359 .addArraySubscriptOperators()
360 .addLoadMethods()
361 .addIncrementCounterMethod()
362 .addDecrementCounterMethod()
363 .completeDefinition();
364 });
365
366 Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "ByteAddressBuffer")
367 .finalizeForwardDeclaration();
368 onCompletion(Record: Decl, Fn: [this](CXXRecordDecl *Decl) {
369 setupBufferType(Decl, S&: *SemaPtr, RC: ResourceClass::SRV, /*IsROV=*/false,
370 /*RawBuffer=*/true)
371 .completeDefinition();
372 });
373 Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWByteAddressBuffer")
374 .finalizeForwardDeclaration();
375 onCompletion(Record: Decl, Fn: [this](CXXRecordDecl *Decl) {
376 setupBufferType(Decl, S&: *SemaPtr, RC: ResourceClass::UAV, /*IsROV=*/false,
377 /*RawBuffer=*/true)
378 .completeDefinition();
379 });
380 Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace,
381 "RasterizerOrderedByteAddressBuffer")
382 .finalizeForwardDeclaration();
383 onCompletion(Record: Decl, Fn: [this](CXXRecordDecl *Decl) {
384 setupBufferType(Decl, S&: *SemaPtr, RC: ResourceClass::UAV, /*IsROV=*/true,
385 /*RawBuffer=*/true)
386 .completeDefinition();
387 });
388}
389
390void HLSLExternalSemaSource::onCompletion(CXXRecordDecl *Record,
391 CompletionFunction Fn) {
392 if (!Record->isCompleteDefinition())
393 Completions.insert(KV: std::make_pair(x: Record->getCanonicalDecl(), y&: Fn));
394}
395
396void HLSLExternalSemaSource::CompleteType(TagDecl *Tag) {
397 if (!isa<CXXRecordDecl>(Val: Tag))
398 return;
399 auto Record = cast<CXXRecordDecl>(Val: Tag);
400
401 // If this is a specialization, we need to get the underlying templated
402 // declaration and complete that.
403 if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(Val: Record))
404 Record = TDecl->getSpecializedTemplate()->getTemplatedDecl();
405 Record = Record->getCanonicalDecl();
406 auto It = Completions.find(Val: Record);
407 if (It == Completions.end())
408 return;
409 It->second(Record);
410}
411

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

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